この記事は何?
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');
導入の参考になりそうな記事
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>
導入の参考になりそうな記事
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
ありの方が圧倒的に良いですね。
要素間を離したい時は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配置された子要素に遠近感を与えます。
回転・遠近あり
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つの横並び要素をそれぞれの中央に配置
<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
(空いた幅を埋める)を設定したことになる。
ホバーすると表示される削除ボタン
.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
を上手く使う - 最初はパクるところから
-
- 最初にざっくり全体のレイアウトを決めてしまうと楽。
- よく使うデザインの早引き的なものがあると良さげ。