この記事は何?
Udemyで20 Web Projects With Vanilla JavaScriptというコースを受講しました。
HTML/CSS/VanillaJSで良い感じなデザインのアプリを20個作っていく、フロントエンド初学者向けのコースです。
コースを進めていく中で、デザインに共通点が多い事に気が付きました。
そこで、今後の自分のためにこのコースで登場するデザインの共通点をまとめることにしました。
受講の動機
HTML/CSS/JavaScriptで10個(くらい)何かを作ってみるチャレンジの一環として受講しました。
何か作ってみるチャレンジの目的は、HTML/CSS/Vanilla JSを使ってとりあえずなにか作ること、手を動かすことです。
プログラミングにおいて手を動かすに勝る学習方法はないと思います。
実感としてもそうですし、そう言っているエンジニアの方も多くいます。
また、質を高めるためにも、まずは絶対的な量が必要とよく言います。
というわけで、色々作ってみることにしました。
しかしその中で、文法はある程度わかっていてもサイトのデザインやレイアウトをどのように考え・作っていけば良いのか全くと言っていいほどわからないことに気が付きました。
そこで、まずは良いデザインとはどういったものなのか?を学びつつ、手を動かせる教材を選びました。
チャレンジのリポジトリ
注意点
初心者向けの記事です。
そんなの常識やん、という内容が多々含まれる恐れがあります。
細心の注意を払ってはいますが、勘違い・ミスがあるかもしれません。見つけたらそっと指摘していただけると幸いです。
「この方法が最適解!」というわけではないです。
本題の前に
VSCodeのTips
いくつか講座内で紹介されていたVSCodeのTipsを紹介します。
拡張機能 Auto Rename Tag
ペアになっているタグを自動でリネームしてくれる拡張機能です。
普通に便利。
拡張機能 Lite Server
VSCode限定というわけではありませんが、VSCodeなら簡単に拡張機能として利用可能なので紹介します。
ファイルの変更を監視・ブラウザに反映してくれる軽量なローカルWebサーバです。
BrowserSyncをSPAに対応出来るようにしたラッパーです。
HTML/CSS/JSファイルの変更を監視、変更されたら即ブラウザに反映してくれます。
Liteというだけあって、ロードが超早いです。
拡張機能の検索欄で「Lite Server」と検索、インストールするだけで導入できます。
HTMLの入力
VSCodeでHTMLを書くときは下の表のように入力すると効率良く入力出来ます。
入力
結果
.active
<div class="active"></div>
タグ.class-name#id-name
<タグ class="class-name" id="id-name"></タグ>
Google Fontの利用
Browse Fonts - Google Fontsから好きなフォントを選んで導入します。
今回はCSSの@importにより利用します。
@import url('https://fonts.googleapis.com/css2?family=Lato:wght@100&family=Roboto:wght@100&display=swap');
導入の参考になりそうな記事
Google Fonts を使ってみよう - Qiita
Rails Googleフォント 使い方(初心者) - Qiita
Font Awesomeの利用
Font Awesomeから好きなアイコンを選んで導入します。
headにlinkを追加して利用出来るようにします。
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.10.2/css/all.min.css" rel="stylesheet">
HTMLで下のように書くだけでアイコンが表示されます!
無料プランだと使用できるアイコンに制限があるようです。
<i class="fas fa-search"></i>
<i class="fas fa-random"></i>
導入の参考になりそうな記事
Font Awesomeの使い方 - Qiita
CSSセレクタ
動画内で多用されていた基本的なセレクタについておさらいします。
実際使うときはネストが深くなりすぎないように注意。
<ul class="showcase">
<li>
<div class="seat"></div>
<small>N/A</small>
</li>
<li>
<div class="seat selected"></div>
<small>Selected</small>
</li>
<li>
<div class="seat occupied"></div>
<small>Occupied</small>
</li>
</ul>
.seat.selected {
background-color: #6feaf6;
}
.seat と .selected を持つ要素が選択される
.seat:not(.occupied):hover {
cursor: pointer;
transform: scale(1.2);
}
.seat の内、 .occupied を持たない要素が選択される
.showcase .seat:not(.occupied):hover {
cursor: default;
transform: scale(1);
}
.showcase の子要素、かつ .seat を持ち、 .occupied を持たない要素が選択される
本題
前置きが長くなりましたが、本題に入っていきます。
例として見ていくレイアウト
映画の座席予約アプリ
レシピ検索アプリ
収支管理アプリ
フィード閲覧アプリ
ニューイヤーカウントダウン
詳細は講師の方のリポジトリを参照してください。
要素内に余白を作りたいときはpaddingを使う
要素内に適度な余白を作りたい時にはpaddingを使います。
中にテキストを持つ要素はほとんどpaddingを使っていました。
paddingあり
paddingなし
paddingあり
paddingなし
paddingあり
paddingなし
paddingありの方が圧倒的に良いですね。
要素間を離したい時はmargin
当然といえば当然ですが、要素サイズの大小に関わらず要素間の距離を開けたい時はmarginを上手く使います。
大きなmarginよりも、小さい要素間の距離を少し開けたい時に使うmarginの方が難しく感じました。
詳しくは後述しますが、N番目の要素にのみmarginを適用することで映画館の座席っぽいデザインを作ることが出来ます。
レイアウトの構成
レイアウト構成については、この教材の場合flexの使用率が圧倒的でした。
レイアウト全体をいくつかの大きめな「ブロック」に分割して構成、bodyにflexを適用して中央寄せ。
という流れが多かったです。
この流れでレイアウトを組んでいくと全体の構成がつかみやすく、どのように全体のレイアウトを組んでいけば良いのかわかりやすかったです。
また、勝手に中央寄せになってくれるので全体的にスッキリします。
border-radiusをよく使う
指定した長さを半径として、ブロックの四隅を丸くするプロパティです。
カクカクしたデザインよりも少し角を丸めたデザインが多かった印象です。
border-radiusあり
border-radiusなし
border-radiusあり
border-radiusなし
また、border-radiusというよりborderの使い方になりますが、下のように画像をborderで囲んでいます。
ただ画像を表示するよりもよりカードっぽく表示することが出来ます。
1番上/下の要素は上/下に余白を作る
1番上/下にいる要素は上/下にmarginを設定することで、不自然に上下に寄らない程度の余白を作っていました。
開発の流れ
JavaScriptによるDOM操作でレイアウトを作る場合、以下の流れが共通していました。
「チュートリアルでやるような小規模なアプリケーションだとこの方がわかりやすい」という話かもしれませんが、実際この流れだと理解しやすかったです。
まずダミーHTML(JavaScriptで作る予定のレイアウト)を書く
ダミーHTMLに対してCSSを書いてスタイルを設定
最後にJavaScriptを書き、DOM操作でレイアウトを作っていく
使えそうなデザイン達
何かしら使えそうなデザイン達をまとめていきます。
映画館の座席っぽく
<div class="row">
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat occupied"></div>
<div class="seat occupied"></div>
<div class="seat"></div>
<div class="seat"></div>
<div class="seat"></div>
</div>
.row {
display: flex;
}
.seat {
background-color: #444451;
height: 12px;
width: 15px;
/* 要素を切り離す */
margin: 3px;
/* 左右上側を丸めてシートっぽくする */
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
四角を作る → marginで切り離す → 左右上側を丸める
ことで作ることができます。
N番目の子要素を選択
映画館の座席なので、下のように端2列は中心4列から少し離したいです。
そこで、 nth-of-typeを使います。
.seat:nth-of-type(2) {
margin-right: 18px
}
.seat:nth-last-of-type(2) {
margin-left: 18px
}
参考
:nth-child() - CSS: カスケーディングスタイルシート | MDN
:nth-of-type() - CSS: カスケーディングスタイルシート | MDN
【CSS】nth-childとnth-of-typeでもう混乱しない。 - Qiita
:nth-of-type()
「指定した要素の親要素」の子要素のうち、指定したセレクタの「N番目」の要素を選択する。
:nth-child()
「指定した要素の親要素」の子要素を見て、セレクタ関係なしに「N番目」の要素を選択する。
カーソルされた時に強調
.seat:not(.occupied):hover {
cursor: pointer;
transform: scale(1.2);
}
立体的なスクリーン
.screen {
background-color: #fff;
height: 70px;
width: 100%;
margin: 15px 0;
transform: rotateX(-45deg);
box-shadow: 0 3px 10px rgba(255, 255, 255, 0.7);
}
.container {
perspective: 1000px;
margin-bottom: 30px;
}
transform でX軸を基準に-45度回転させ、
perspective で3D配置された子要素に遠近感を与えます。
CSS perspectiveを使ったデザイン - Qiita
回転・遠近なし
回転・遠近あり
X軸を基準に若干傾き、3D風になっています。
検索欄
<div class="flex">
<form class="flex" id="submit">
<input type="text" id="search" placeholder="Search for meals or keywords">
<button class="search-btn" id="submit" type="submit">
<i class="fas fa-search"></i>
</button>
</form>
<button class="random-btn" id="random">
<i class="fas fa-random"></i>
</button>
</div>
完成形
.flex {
display: flex;
}
input,
button {
border: 1px solid #dedede;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
font-size: 14px;
padding: 8px 10px;
margin: 0;
}
input[type="text"] {
width: 300px;
}
input 、2つの button を .flex で囲って横並びに
共通でborder をセット、左側の角を丸める
.search-btn {
cursor: pointer;
border-left: 0;
border-radius: 0;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
.search-btn の border を削除、右側だけ丸める
.random-btn {
cursor: pointer;
margin-left: 10px;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
random-btn は検索欄から少しだけ離し、右側も丸める。共通で左側を丸めてあるので、両側が丸められる。
画像をカード風に表示する
<div id="meals" class="meals">
<div class="meal">
<img src="https://www.themealdb.com/images/media/meals/ysxwuq1487323065.jpg" alt="Fish pie">
<div class="meal-info" data-mealid="52802">
<h3>Fish pie</h3>
</div>
</div>
<div class="meal">
<img src="https://www.themealdb.com/images/media/meals/a15wsa1614349126.jpg" alt="Fish fofos">
<div class="meal-info" data-mealid="53043">
<h3>Fish fofos</h3>
</div>
</div>
</div>
.meals {
display: grid;
grid-template-columns: repeat(4, 1fr);
/* grid 同士の距離 */
grid-gap: 20px;
margin-top: 20px;
}
grid で4列表示にして間隔を離す。
.meal {
cursor: pointer;
position: relative;
height: 180px;
width: 180px;
text-align: center;
}
元画像のサイズで表示されないようにサイズに制限をかける
absoluteを子要素で利用するため、 relative にしておく
.meal img {
width: 100%;
height: 100%;
border: 4px #fff solid;
border-radius: 2px;
}
画像の周りをボーダーで囲って丸める → 画像オンリーよりもカードっぽく
幅と高さを親要素 meal に対する100%に
.meal-info {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
transition: opacity 0.2s ease-in;
opacity: 0;
}
.meal:hover .meal-info {
opacity: 1;
}
meal > img & meal-infoなので、 meal-info は本来画像の下側に表示される。
今回はホバーしたら meal-info が表示される形にしたいので、 absolute を使って親要素の真上に meal-info を持ってくる。
そして、ホバーされた時に opacity が1になるようにする。
メディアクエリ
@media(max-width: 800px) {
.meals {
grid-template-columns: repeat(3, 1fr);
}
}
@media(max-width: 700px) {
.meals {
grid-template-columns: repeat(2, 1fr);
}
.meal {
height: 200px;
width: 200px;
}
}
@media(max-width: 500px) {
input[type="text"] {
width: 100%;
}
.meals {
grid-template-columns: repeat(1fr);
}
.meal {
height: 300px;
width: 300px;
}
}
幅に応じて grid 、 meal の幅を変更することでレスポンシブに。
材料の一覧表示(整列されていないリスト)
displayをいじってあえて整列していないリストにすることで、一覧性を高める事ができます。
ul {
padding-left: 0;
list-style-type: none;
}
ul li {
border: 1px solid #dedede;
border-radius: 5px;
background-color: #fff;
display: inline-block;
color: #2d2013;
font-size: 12px;
font-weight: bold;
padding: 5px;
margin: 0 5px 5px 0;
}
2つの横並び要素をそれぞれの中央に配置
完成形
INCOMEとEXPENSEをそれぞれ中央に配置したい。
<div class="inc-exp-container">
<div>
<h4>Income</h4>
<p id="money-plus" class="money plus">+$0.00</p>
</div>
<div>
<h4>Expense</h4>
<p id="money-minus" class="money minus">-$0.00</p>
</div>
</div>
.inc-exp-container {
background-color: #fff;
box-shadow: var(--box-shadow); /* 変数を利用 */
padding: 20px;
display: flex;
justify-content: space-between;
}
flex + space-between でコンテナの両サイドに表示。
.inc-exp-container div {
flex: 1;
text-align: center;
}
.inc-exp-container div:first-of-type {
border-right: 1px solid #dedede;
}
flex: 1 で隙間を埋めることで中央寄せする。
flex: 1;は一括指定プロパティ。
値が一つの場合は grow (空いた幅を埋める)を設定したことになる。
flex-grow
flex-shrink
flex-basis
ホバーすると表示される削除ボタン
.delete-btn {
cursor: pointer;
background-color: #e74c3c;
border: 0;
color: #fff;
font-size: 20px;
line-height: 20px;
padding: 2px 5px;
position: absolute;
top: 50%;
left: 0;
transform: translate(-100%, -50%);
transition: opacity 0.3s ease;
opacity: 0;
}
.list li:hover .delete-btn {
opacity: 1;
}
transform: translate(-100%, -50%);でXY位置を調整している。
丸数字を表示
<div class="post">
<div class="number">1</div>
<div class="post-info">
<h2 class="post-title">sunt aut facere repellat provident occaecati excepturi optio reprehenderit</h2>
<p class="post-body">quia et suscipit
suscipit recusandae consequuntur expedita et cum
reprehenderit molestiae ut ut quas totam
nostrum rerum est autem sunt rem eveniet architecto</p>
</div>
</div>
.number {
position: absolute;
top: -15px;
left: -15px;
font-size: 15px;
width: 40px;
height: 40px;
border-radius: 50%;
background: #fff;
color: #296ca8;
display: flex;
align-items: center;
justify-content: center;
padding: 7px 10px;
}
post に対して absolute + -15px → 左上に固定
flex で中央寄せ → numberの中身を中央に寄せる
丸がバウンドするアニメーション
.circle {
background-color: #fff;
width: 10px;
height: 10px;
border-radius: 50%;
margin: 5px;
animation: bounce 0.5s ease-in infinite;
}
.circle:nth-of-type(2) {
animation-delay: 0.1s;
}
.circle:nth-of-type(3) {
animation-delay: 0.2s;
}
@keyframes bounce {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-10px);
}
}
丸を作る → アニメーションを設定 → ディレイを入れるだけです。
背景を画像にする
body {
background-image: url('https://images.unsplash.com/photo-1467810563316-b5476525c0f9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1349&q=80');
/* 背景画像を1枚のみにする */
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
height: 100vh;
color: #fff;
font-family: "Lato", sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
margin: 0;
overflow: hidden;
}
背景画像を暗くする
body::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
}
疑似要素に対して background-color を割り当てることで暗くします。
文字まで暗くなってしまっているので修正します。
body * {
z-index: 1;
}
z-index の指定により背景以外が暗くならないようにします。
まとめ
余白は大事!!
paddingとmarginを上手く使う
最初はパクるところから
最初にざっくり全体のレイアウトを決めてしまうと楽。
よく使うデザインの早引き的なものがあると良さげ。
↧