【HTML/CSS】構文チェックとリファクタリングのやり方|簡単なポートフォリオサイトの作成
HTML/CSSのコードを綺麗で管理しやすくする「リファクタリング」の基本を解説します。CSS変数を使って色やサイズを一元管理する方法や、共通スタイルをクラス化してコードの重複を減らすテクニックを学びます。これにより、保守性が高く読みやすいコードの書き方が身につきます。
開発環境
- OS: Windows10
- Editor: Visual Studio Code
- HTML: 5
- CSS: 3
- JavaScript: ES6以降
サンプルコード
/assets/css/style.css
/assets/css/style.css1 /* リセット・基本設定 */ 2 * { 3- box-sizing: border-box; 4- margin: 0; 5- padding: 0; 6+ box-sizing: border-box; 7+ margin: 0; 8+ padding: 0; 9 } 10 11 body { 12- font-family: sans-serif; 13- line-height: 1.6; 14- color: #333; 15- background-color: #fff; 16+ font-family: sans-serif; 17+ line-height: 1.6; 18+ color: #333; 19+ background-color: #fff; 20 } 21 22 a { 23- text-decoration: none; 24- color: inherit; 25+ text-decoration: none; 26+ color: inherit; 27+} 28+ 29+/* 共通変数 */ 30+:root { 31+ --main-padding: 80px 20px; 32+ --container-width: 960px; 33+ --container-margin: 0 auto; 34+ --main-bg: #f5f5f5; 35+ --sub-bg: #f9f9f9; 36+} 37+ 38+/* 共通スタイル */ 39+.container { 40+ width: 90%; 41+ max-width: var(--container-width); 42+ margin: var(--container-margin); 43+} 44+ 45+.section-title { 46+ font-size: 28px; 47+ text-align: center; 48+ margin-bottom: 50px; 49+} 50+ 51+section { 52+ padding: var(--main-padding); 53 } 54 55 /* ヘッダー全体のスタイル */ 56 header { 57- background-color: #f5f5f5; 58- padding: 20px 0; 59- border-bottom: 1px solid #ddd; 60- position: fixed; 61- top: 0; 62- left: 0; 63- width: 100%; 64- z-index: 1000; 65+ background-color: var(--main-bg); 66+ padding: 20px 0; 67+ border-bottom: 1px solid #ddd; 68+ position: fixed; 69+ top: 0; 70+ left: 0; 71+ width: 100%; 72+ z-index: 1000; 73 } 74 75 /* ヘッダー内のコンテンツを横並びに */ 76 header .container { 77- width: 90%; 78- max-width: 960px; 79- margin: 0 auto; 80- display: flex; 81- justify-content: space-between; 82- align-items: center; 83+ display: flex; 84+ justify-content: space-between; 85+ align-items: center; 86 } 87 88 /* ロゴ */ 89 .logo { 90- font-size: 24px; 91- font-weight: bold; 92- color: #333; 93+ font-size: 24px; 94+ font-weight: bold; 95+ color: #333; 96 } 97 98 /* ナビゲーション */ 99 .nav-list { 100- list-style: none; 101- display: flex; 102- gap: 20px; 103+ list-style: none; 104+ display: flex; 105+ gap: 20px; 106 } 107 108 .nav-list li a { 109- font-weight: bold; 110- transition: color 0.3s; 111+ font-weight: bold; 112+ transition: color 0.3s; 113 } 114 115 .nav-list li a:hover { 116- color: #0077cc; 117+ color: #0077cc; 118 } 119 120 /* メインビジュアル */ 121 #main-visual { 122- height: 80vh; 123- background-image: url("../img/main-visual.jpg"); 124- background-size: cover; 125- background-position: center; 126- display: flex; 127- align-items: center; 128- justify-content: center; 129- text-align: center; 130- position: relative; 131- color: #fff; 132+ height: 80vh; 133+ background-image: url("../img/main-visual.jpg"); 134+ background-size: cover; 135+ background-position: center; 136+ display: flex; 137+ align-items: center; 138+ justify-content: center; 139+ text-align: center; 140+ position: relative; 141+ color: #fff; 142 } 143 144 #main-visual::before { 145- content: ""; 146- position: absolute; 147- inset: 0; 148- background-color: rgba(0, 0, 0, 0.4); /* 黒の半透明オーバーレイ */ 149- z-index: 1; 150+ content: ""; 151+ position: absolute; 152+ inset: 0; 153+ background-color: rgba(0,0,0,0.4); 154+ z-index: 1; 155 } 156 157 .main-text { 158- position: relative; 159- z-index: 2; 160+ position: relative; 161+ z-index: 2; 162 } 163 164 .main-text h2 { 165- font-size: 36px; 166- margin-bottom: 10px; 167- text-shadow: 1px 1px 4px rgba(0, 0, 0, 0.6); 168+ font-size: 36px; 169+ margin-bottom: 10px; 170+ text-shadow: 1px 1px 4px rgba(0,0,0,0.6); 171 } 172 173 .main-text p { 174- font-size: 18px; 175- text-shadow: 1px 1px 4px rgba(0, 0, 0, 0.6); 176+ font-size: 18px; 177+ text-shadow: 1px 1px 4px rgba(0,0,0,0.6); 178 } 179 180 /* Aboutセクション */ 181 #about { 182- padding: 80px 20px; 183- background-color: #f9f9f9; 184+ background-color: var(--sub-bg); 185 } 186 187-.about-content { 188- display: flex; 189- align-items: center; 190- gap: 40px; 191- max-width: 960px; 192- margin: 0 auto; 193- flex-wrap: wrap; 194+#about .container { 195+ display: flex; 196+ align-items: center; 197+ gap: 40px; 198+ flex-wrap: wrap; 199 } 200 201 .about-image img { 202- width: 250px; 203- border-radius: 12px; 204- box-shadow: 0 4px 12px rgba(0,0,0,0.1); 205+ width: 250px; 206+ border-radius: 12px; 207+ box-shadow: 0 4px 12px rgba(0,0,0,0.1); 208 } 209 210 .about-text { 211- flex: 1; 212- min-width: 280px; 213+ flex: 1; 214+ min-width: 280px; 215 } 216 217 .about-text h2 { 218- font-size: 28px; 219- margin-bottom: 20px; 220+ font-size: 28px; 221+ margin-bottom: 20px; 222 } 223 224 .about-text p { 225- margin-bottom: 16px; 226- line-height: 1.8; 227+ margin-bottom: 16px; 228+ line-height: 1.8; 229 } 230 231 /* --- Skillsセクション --- */ 232 #skills { 233- padding: 80px 20px; 234- background-color: #f5f5f5; 235-} 236- 237-.section-title { 238- font-size: 28px; 239- text-align: center; 240- margin-bottom: 50px; 241+ background-color: var(--main-bg); 242 } 243 244 .skills-list { 245- max-width: 960px; 246- margin: 0 auto; 247- display: grid; 248- grid-template-columns: 1fr 1fr; 249- gap: 30px; 250- list-style: none; 251- padding: 0; 252+ display: grid; 253+ grid-template-columns: 1fr 1fr; 254+ gap: 30px; 255+ list-style: none; 256+ padding: 0; 257 } 258 259 .skills-list li { 260- background-color: #fff; 261- border: 1px solid #ddd; 262- padding: 20px; 263- border-radius: 8px; 264- box-shadow: 0 2px 8px rgba(0,0,0,0.05); 265- display: flex; 266- align-items: flex-start; 267- gap: 20px; 268+ background-color: #fff; 269+ border: 1px solid #ddd; 270+ padding: 20px; 271+ border-radius: 8px; 272+ box-shadow: 0 2px 8px rgba(0,0,0,0.05); 273+ display: flex; 274+ align-items: flex-start; 275+ gap: 20px; 276 } 277 278 .skill-icon img { 279- width: 48px; 280- height: 48px; 281- object-fit: contain; 282+ width: 48px; 283+ height: 48px; 284+ object-fit: contain; 285 } 286 287 .skill-text h3 { 288- font-size: 18px; 289- margin-bottom: 10px; 290- color: #333; 291+ font-size: 18px; 292+ margin-bottom: 10px; 293+ color: #333; 294 } 295 296 .skill-text p { 297- font-size: 14px; 298- color: #666; 299- line-height: 1.6; 300+ font-size: 14px; 301+ color: #666; 302+ line-height: 1.6; 303 } 304 305 /* Worksセクション */ 306 #works { 307- padding: 80px 20px; 308- background-color: #fff; 309+ background-color: var(--sub-bg); 310 } 311 312 .works-grid { 313- max-width: 960px; 314- margin: 0 auto; 315- display: grid; 316- grid-template-columns: repeat(3, 1fr); 317- gap: 30px; 318+ display: grid; 319+ grid-template-columns: repeat(3, 1fr); 320+ gap: 30px; 321 } 322 323 .work-card { 324- display: block; 325- border-radius: 8px; 326- overflow: hidden; 327- box-shadow: 0 4px 12px rgba(0,0,0,0.1); 328- text-decoration: none; 329- color: inherit; 330- transition: transform 0.3s ease, box-shadow 0.3s ease; 331+ display: block; 332+ border-radius: 8px; 333+ overflow: hidden; 334+ box-shadow: 0 4px 12px rgba(0,0,0,0.1); 335+ text-decoration: none; 336+ color: inherit; 337+ transition: transform 0.3s ease, box-shadow 0.3s ease; 338 } 339 340 .work-card:hover { 341- transform: translateY(-5px); 342- box-shadow: 0 8px 20px rgba(0,0,0,0.15); 343+ transform: translateY(-5px); 344+ box-shadow: 0 8px 20px rgba(0,0,0,0.15); 345 } 346 347 .work-card img { 348- width: 100%; 349- height: 180px; 350- object-fit: cover; 351- display: block; 352+ width: 100%; 353+ height: 180px; 354+ object-fit: cover; 355+ display: block; 356 } 357 358 .work-info { 359- padding: 15px 20px; 360+ padding: 15px 20px; 361 } 362 363 .work-info h3 { 364- font-size: 18px; 365- margin-bottom: 8px; 366- color: #333; 367+ font-size: 18px; 368+ margin-bottom: 8px; 369+ color: #333; 370 } 371 372 .work-info p { 373- font-size: 14px; 374- color: #666; 375- line-height: 1.5; 376+ font-size: 14px; 377+ color: #666; 378+ line-height: 1.5; 379 } 380 381 /* contactセクション */ 382 #contact { 383- padding: 80px 20px; 384- background-color: #f9f9f9; 385+ background-color: var(--sub-bg); 386 } 387 388 .contact-form { 389- max-width: 600px; 390- margin: 0 auto; 391- display: flex; 392- flex-direction: column; 393- gap: 20px; 394+ max-width: 600px; 395+ margin: 0 auto; 396+ display: flex; 397+ flex-direction: column; 398+ gap: 20px; 399 } 400 401 .contact-form label { 402- font-weight: 600; 403- margin-bottom: 6px; 404- display: block; 405- color: #333; 406+ font-weight: 600; 407+ margin-bottom: 6px; 408+ display: block; 409+ color: #333; 410 } 411 412 .required { 413- color: red; 414- margin-left: 4px; 415+ color: red; 416+ margin-left: 4px; 417 } 418 419 .contact-form input, 420 .contact-form textarea { 421- padding: 12px 16px; 422- border: 1px solid #ccc; 423- border-radius: 6px; 424- font-size: 16px; 425- resize: vertical; 426- font-family: inherit; 427+ padding: 12px 16px; 428+ border: 1px solid #ccc; 429+ border-radius: 6px; 430+ font-size: 16px; 431+ resize: vertical; 432+ font-family: inherit; 433 } 434 435 .contact-form input:focus, 436 .contact-form textarea:focus { 437- border-color: #0077cc; 438- outline: none; 439+ border-color: #0077cc; 440+ outline: none; 441 } 442 443 .btn-submit { 444- background-color: #0077cc; 445- color: white; 446- padding: 14px 0; 447- font-size: 18px; 448- border: none; 449- border-radius: 8px; 450- cursor: pointer; 451- transition: background-color 0.3s ease; 452- font-weight: 600; 453+ background-color: #0077cc; 454+ color: white; 455+ padding: 14px 0; 456+ font-size: 18px; 457+ border: none; 458+ border-radius: 8px; 459+ cursor: pointer; 460+ transition: background-color 0.3s ease; 461+ font-weight: 600; 462 } 463 464 .btn-submit:hover { 465- background-color: #005fa3; 466+ background-color: #005fa3; 467 } 468 469 /* Footer 全体構造 */ 470 #footer { 471- margin-top: 80px; 472+ margin-top: 80px; 473 } 474 475 /* 上部:Back to Top ボタン部分 */ 476 .footer-top { 477- background-color: #555; 478- padding: 12px 20px; 479- text-align: center; 480+ background-color: #555; 481+ padding: 12px 20px; 482+ text-align: center; 483 } 484 485 .back-to-top { 486- display: inline-block; 487- width: 24px; 488- height: 24px; 489- line-height: 24px; 490- background-color: #fff; 491- color: #555; 492- border-radius: 50%; 493- text-align: center; 494- font-size: 12px; 495- text-decoration: none; 496- font-weight: bold; 497- transition: background-color 0.3s ease, color 0.3s ease; 498+ display: inline-block; 499+ width: 24px; 500+ height: 24px; 501+ line-height: 24px; 502+ background-color: #fff; 503+ border-radius: 50%; 504+ text-align: center; 505+ font-size: 12px; 506+ text-decoration: none; 507+ font-weight: bold; 508+ transition: background-color 0.3s ease, color 0.3s ease; 509 } 510 511 .back-to-top:hover { 512- background-color: #eee; 513- color: #222; 514+ background-color: #eee; 515+ color: #222; 516 } 517 518 /* 下部:コピーライト部分 */ 519 .footer-bottom { 520- background-color: #333; 521- padding: 20px 20px; 522- text-align: center; 523+ background-color: #333; 524+ padding: 20px 20px; 525+ text-align: center; 526 } 527 528 .footer-bottom p { 529- font-size: 14px; 530- color: #ccc; 531- margin: 0; 532+ font-size: 14px; 533+ color: #ccc; 534+ margin: 0; 535 } 536 537 /* ハンバーガーアイコン */ 538 .hamburger { 539- display: none; 540- flex-direction: column; 541- justify-content: space-between; 542- width: 28px; 543- height: 20px; 544- background: none; 545- border: none; 546- cursor: pointer; 547- padding: 0; 548- z-index: 1001; 549- position: absolute; 550- top: 20px; 551- right: 20px; 552+ display: none; 553+ flex-direction: column; 554+ justify-content: space-between; 555+ width: 28px; 556+ height: 20px; 557+ background: none; 558+ border: none; 559+ cursor: pointer; 560+ padding: 0; 561+ z-index: 1001; 562+ position: absolute; 563+ top: 20px; 564+ right: 20px; 565 } 566 567 .hamburger span { 568- display: block; 569- height: 3px; 570- background-color: #333; 571- border-radius: 2px; 572- transition: transform 0.3s ease, opacity 0.3s ease; 573+ display: block; 574+ height: 3px; 575+ background-color: #333; 576+ border-radius: 2px; 577+ transition: transform 0.3s ease, opacity 0.3s ease; 578 } 579 580 .hamburger.active span:nth-child(1) { 581- transform: rotate(45deg) translate(6px, 6px); 582+ transform: rotate(45deg) translate(6px, 6px) 583 } 584 585 .hamburger.active span:nth-child(2) { 586- opacity: 0; 587+ opacity: 0; 588 } 589 590 .hamburger.active span:nth-child(3) { 591- transform: rotate(-45deg) translate(6px, -6px); 592+ transform: rotate(-45deg) translate(6px, -6px) 593 } 594 595-/* レスポンシブ対応 */ 596 @media (max-width: 768px) { 597- /* ヘッダー:ナビを縦並びに */ 598- header .container { 599- flex-direction: column; 600- gap: 10px; 601- } 602- 603- .nav-list { 604- flex-direction: column; 605- align-items: center; 606- gap: 24px; 607- } 608- 609- .nav-list a { 610- padding: 10px 20px; 611- display: inline-block; 612- } 613- 614- .main-text h2 { 615- font-size: 24px; 616- } 617- 618- .main-text p { 619- font-size: 14px; 620- } 621- 622- /* About:縦並びに */ 623- .about-content { 624- flex-direction: column; 625- text-align: center; 626- } 627- 628- .about-image img { 629- width: 80%; 630- max-width: 300px; 631- margin: 0 auto; 632- } 633- 634- /* Skills:1カラムに */ 635- .skills-list { 636- grid-template-columns: 1fr; 637- } 638- 639- /* Works:1〜2カラムに */ 640- .works-grid { 641- grid-template-columns: 1fr; 642- } 643- 644- /* Contact:フォーム幅調整(不要な場合は省略) */ 645- .contact-form { 646- width: 100%; 647- } 648- 649- .hamburger { 650- display: flex; 651- } 652- 653- .nav { 654- display: none; 655- flex-direction: column; 656- align-items: center; 657- position: absolute; 658- top: 70px; 659- right: 0; 660- width: 100%; 661- height: 100vh; 662- background-color: #fff; 663- padding: 40px 0; 664- box-shadow: 0 4px 12px rgba(0,0,0,0.1); 665- z-index: 1000; 666- } 667- 668- .nav.active { 669- display: flex; 670- } 671-} 672+ /* ヘッダー:ナビを縦並びに */ 673+ header .container { 674+ flex-direction: column; 675+ gap: 10px; 676+ } 677+ .nav-list { 678+ flex-direction: column; 679+ align-items: center; 680+ gap: 24px; 681+ } 682+ .nav-list a { 683+ padding: 10px 20px; 684+ display: inline-block; 685+ } 686+ 687+ .main-text h2 { 688+ font-size: 24px; 689+ } 690+ 691+ .main-text p { 692+ font-size: 14px; 693+ } 694+ 695+ #about .container { 696+ flex-direction: column; 697+ text-align: center; 698+ } 699+ 700+ .about-image img { 701+ width: 80%; 702+ max-width: 300px; 703+ margin: 0 auto; 704+ } 705+ 706+ .skills-list { 707+ grid-template-columns: 1fr; 708+ } 709+ 710+ .works-grid { 711+ grid-template-columns: 1fr; 712+ } 713+ 714+ .contact-form { 715+ width: 100%; 716+ } 717+ 718+ .hamburger { 719+ display: flex; 720+ } 721+ 722+ .nav { 723+ display: none; 724+ flex-direction: column; 725+ align-items: center; 726+ position: absolute; 727+ top: 70px; 728+ right: 0; 729+ width: 100%; 730+ height: 100vh; 731+ background-color: #fff; 732+ padding: 40px 0; 733+ box-shadow: 0 4px 12px rgba(0,0,0,0.1); 734+ z-index: 1000; 735+ } 736+ 737+ .nav.active { 738+ display: flex; 739+ } 740+} 741
/index.html
/index.html1 <header> 2 <div class="container"> 3 <h1 class="logo">My Portfolio</h1> 4- 5 <!-- ハンバーガーアイコン --> 6 <button class="hamburger" id="hamburger" aria-label="メニューを開く"> 7 <span></span> 8 <section id="main-visual"> 9 <div class="main-text"> 10 <h2>Welcome to My Portfolio</h2> 11- <p>フロントエンドとWebデザインの制作事例を紹介します</p> 12+ <p>ポートフォリオサイトへようこそ</p> 13 </div> 14 </section> 15 16 <section id="about"> 17- <div class="container about-content"> 18+ <div class="container"> 19 <div class="about-image"> 20- <img src="assets/img/profile.jpg" alt="プロフィール画像"> 21+ <img src="assets/img/profile.jpg" alt="プロフィール画像" width="200" height="200"> 22 </div> 23 <div class="about-text"> 24 <h2>About</h2> 25- <p>テキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト</p> 26 <p>テキストテキストテキストテキストテキストテキストテキストテキストテキストテキスト</p> 27+ <p>テキストテキストテキストテキストテキストテキスト</p> 28 </div> 29 </div> 30 </section> 31 <ul class="skills-list"> 32 <li> 33 <div class="skill-icon"> 34- <img src="assets/img/icon-html.png" alt="HTML"> 35+ <img src="assets/img/icon-html.png" alt="HTML" width="64" height="64"> 36 </div> 37 <div class="skill-text"> 38 <h3>HTML / CSS</h3> 39- <p>レスポンシブ対応やアニメーションを含めたWebサイトのコーディングが可能です。</p> 40+ <p>テキストテキストテキストテキストテキストテキストテキストテキスト</p> 41 </div> 42 </li> 43 <li> 44 <div class="skill-icon"> 45- <img src="assets/img/icon-js.png" alt="JavaScript"> 46+ <img src="assets/img/icon-js.png" alt="JavaScript" width="64" height="64"> 47 </div> 48 <div class="skill-text"> 49 <h3>JavaScript</h3> 50- <p>スライダーやモーダル、タブ切り替えなどの動的UIを実装できます。</p> 51+ <p>テキストテキストテキストテキストテキストテキストテキストテキスト</p> 52 </div> 53 </li> 54 <li> 55 <div class="skill-icon"> 56- <img src="assets/img/icon-wp.png" alt="WordPress"> 57+ <img src="assets/img/icon-react.png" alt="React" width="64" height="64"> 58 </div> 59 <div class="skill-text"> 60- <h3>WordPress</h3> 61- <p>オリジナルテーマの作成やカスタマイズが可能です。</p> 62+ <h3>React</h3> 63+ <p>テキストテキストテキストテキストテキストテキストテキストテキスト</p> 64 </div> 65 </li> 66 <li> 67 <div class="skill-icon"> 68- <img src="assets/img/icon-react.png" alt="React"> 69+ <img src="assets/img/icon-wp.png" alt="WordPress" width="64" height="64"> 70 </div> 71 <div class="skill-text"> 72- <h3>React</h3> 73- <p>Webデザインの作成や、他メンバーとの連携を意識した設計ができます。</p> 74+ <h3>WordPress</h3> 75+ <p>テキストテキストテキストテキストテキストテキストテキストテキスト</p> 76 </div> 77 </li> 78 </ul> 79 <div class="container"> 80 <h2 class="section-title">Works</h2> 81 <div class="works-grid"> 82- <a href="https://example.com/project1" class="work-card" target="_blank" rel="noopener noreferrer"> 83- <img src="assets/img/work1.jpg" alt="Project 1"> 84+ <a href="/project1" class="work-card" target="_blank" rel="noopener noreferrer"> 85+ <img src="assets/img/work1.jpg" alt="Project 1" width="400" height="300"> 86 <div class="work-info"> 87 <h3>Project 1</h3> 88- <p>コーポレートサイトのリニューアル制作</p> 89+ <p>テキストテキストテキストテキストテキストテキスト</p> 90 </div> 91 </a> 92- <a href="https://example.com/project2" class="work-card" target="_blank" rel="noopener noreferrer"> 93- <img src="assets/img/work2.jpg" alt="Project 2"> 94+ <a href="/project2" class="work-card" target="_blank" rel="noopener noreferrer"> 95+ <img src="assets/img/work2.jpg" alt="Project 2" width="400" height="300"> 96 <div class="work-info"> 97 <h3>Project 2</h3> 98- <p>ECサイト構築およびカスタマイズ</p> 99+ <p>テキストテキストテキストテキストテキストテキスト</p> 100 </div> 101 </a> 102- <a href="https://example.com/project3" class="work-card" target="_blank" rel="noopener noreferrer"> 103- <img src="assets/img/work3.jpg" alt="Project 3"> 104+ <a href="/project3" class="work-card" target="_blank" rel="noopener noreferrer"> 105+ <img src="assets/img/work3.jpg" alt="Project 3" width="400" height="300"> 106 <div class="work-info"> 107 <h3>Project 3</h3> 108- <p>ポートフォリオサイトのデザイン・コーディング</p> 109+ <p>テキストテキストテキストテキストテキストテキスト</p> 110 </div> 111 </a> 112 </div> 113 <label for="message">お問い合わせ内容<span class="required">*</span></label> 114 <textarea id="message" name="message" rows="6" required></textarea> 115 116- <button type="submit" class="btn-submit">送信する</button> 117+ <button type="submit" class="btn-submit">送信</button> 118 </form> 119 </div> 120 </section> 121 </div> 122 <div class="footer-bottom"> 123 <div class="container"> 124- <p class="copyright">© 2025 YourName</p> 125+ <p class="copyright">© 2025 My Portfolio</p> 126 </div> 127 </div> 128 </footer> 129 130 <script> 131- const hamburger = document.getElementById('hamburger'); 132- const nav = document.getElementById('nav'); 133- const navLinks = document.querySelectorAll('.nav-list a'); 134+ const hamburger = document.getElementById("hamburger"); 135+ const nav = document.getElementById("nav"); 136+ const navLinks = document.querySelectorAll(".nav-list a"); 137 138 // メニュー開閉(ボタン) 139- hamburger.addEventListener('click', () => { 140- nav.classList.toggle('active'); 141- hamburger.classList.toggle('active'); // ← これを追加! 142+ hamburger.addEventListener("click", () => { 143+ nav.classList.toggle("active"); 144+ hamburger.classList.toggle("active"); 145 }); 146 147 // リンククリックでメニューを閉じる 148 navLinks.forEach(link => { 149- link.addEventListener('click', () => { 150- nav.classList.remove('active'); 151- hamburger.classList.remove('active'); // ← これも追加! 152+ link.addEventListener("click", () => { 153+ nav.classList.remove("active"); 154+ hamburger.classList.remove("active"); 155 }); 156 }); 157 </script> 158- 159 </body> 160 161 </html> 162
コード解説
変更点: CSS変数を導入し、色やサイズを一元管理
1+/* 共通変数 */ 2+:root { 3+ --main-padding: 80px 20px; 4+ --container-width: 960px; 5+ --container-margin: 0 auto; 6+ --main-bg: #f5f5f5; 7+ --sub-bg: #f9f9f9; 8+} 9 10 /* ヘッダー全体のスタイル */ 11 header { 12- background-color: #f5f5f5; 13+ background-color: var(--main-bg); 14 padding: 20px 0; 15 border-bottom: 1px solid #ddd; 16 position: fixed;
CSSに変数が導入されました。これは、サイト全体で繰り返し使う色やサイズなどの値を、一箇所にまとめて管理するための機能です。
:root というセレクタの中に、--から始まる名前(例: --main-bg)で変数を定義します。ここでは、メインの背景色や余白のサイズなどを変数として定義しています。
実際にスタイルを適用する際は、var()という関数を使って定義した変数を呼び出します。例えば、background-color: var(--main-bg);と書くことで、:rootで定義した#f5f5f5という色が適用されます。
この方法を使うと、後からサイト全体のデザインを変更したくなった時に、:rootの中の値を変更するだけで、その変数が使われている全ての箇所のスタイルを一括で変更できるため、修正が非常に楽になります。
変更点: 共通レイアウトをクラス化し、コードの重複を削減
1+/* 共通スタイル */ 2+.container { 3+ width: 90%; 4+ max-width: var(--container-width); 5+ margin: var(--container-margin); 6+} 7+ 8+.section-title { 9+ font-size: 28px; 10+ text-align: center; 11+ margin-bottom: 50px; 12+} 13+ 14+section { 15+ padding: var(--main-padding); 16+}
サイト内の様々な場所で使われる共通のスタイルを、独立したクラスとして定義しました。これを「共通化」や「クラス化」と呼びます。
例えば、多くのセクションでコンテンツの横幅を揃え、中央に配置するために同じようなスタイルが書かれていました。そのスタイルを.containerという一つのクラスにまとめました。同様に、各セクションの見出しのデザインを.section-titleに、各セクションの上下の余白をsectionタグセレクタにまとめています。
このように共通のスタイルをまとめておくことで、同じコードを何度も書く必要がなくなり、CSSファイル全体がスッキリと短くなります。また、どの要素がどのような役割を持っているのかが分かりやすくなり、管理しやすくなるというメリットもあります。
変更点: 共通クラスを適用し、CSSとHTMLの構造を整理
/index.html1 <section id="about"> 2- <div class="container about-content"> 3+ <div class="container"> 4 <div class="about-image"> 5 <img src="assets/img/profile.jpg" alt="プロフィール画像"> 6 </div>
/assets/css/style.css1 #about { 2- padding: 80px 20px; 3- background-color: #f9f9f9; 4+ background-color: var(--sub-bg); 5 } 6 7-.about-content { 8- display: flex; 9- align-items: center; 10- gap: 40px; 11- max-width: 960px; 12- margin: 0 auto; 13- flex-wrap: wrap; 14-} 15+#about .container { 16+ display: flex; 17+ align-items: center; 18+ gap: 40px; 19+ flex-wrap: wrap; 20+}
先ほど作成した共通クラス.containerを、実際にHTMLの要素に適用しました。
変更前は、#aboutセクションの中にあったdiv要素に.about-contentという独自のクラスが使われていました。CSS側でも、この.about-contentに対して横幅や中央配置のスタイルを指定していました。
変更後では、HTMLのクラス名を共通の.containerに書き換えました。これにより、CSS側では.containerに定義済みの横幅や中央配置のスタイルが自動的に適用されるため、.about-contentに書かれていたmax-widthやmarginといった重複した記述を削除できました。
このように共通クラスを使うことで、HTMLの構造がシンプルになり、CSSのコード量も削減され、より効率的にスタイリングができるようになります。
変更点: セクションタイトルを共通クラスに統一
/assets/css/style.css1 /* --- Skillsセクション --- */ 2 #skills { 3- padding: 80px 20px; 4- background-color: #f5f5f5; 5-} 6- 7-.section-title { 8- font-size: 28px; 9- text-align: center; 10- margin-bottom: 50px; 11+ background-color: var(--main-bg); 12 }
#skillsセクション内に個別に定義されていた.section-titleのスタイルが削除されました。
これは、先ほどの「共通スタイルのクラス化」によって、.section-titleの定義がCSSファイルの上部にある共通エリアに移動したためです。
以前のコードでは、セクションごとに見出しのスタイルを定義する必要がありましたが、共通化したことで、HTML側で<h2 class="section-title">と書くだけで、サイト内のどの見出しにも同じデザインを適用できるようになりました。これにより、コードの重複がなくなり、デザインの一貫性も保ちやすくなります。
変更点: 画像の表示崩れを防ぐための属性追加
/index.html1 <div class="about-image"> 2- <img src="assets/img/profile.jpg" alt="プロフィール画像"> 3+ <img src="assets/img/profile.jpg" alt="プロフィール画像" width="200" height="200"> 4 </div>
HTMLの<img>タグに、width(幅)とheight(高さ)の属性が追加されました。
これらの属性をHTMLに直接記述することで、ブラウザはCSSや画像の読み込みが完了する前に、その画像がどのくらいの大きさで表示されるかを予測し、あらかじめその分のスペースを確保することができます。
もしこの記述がないと、画像の読み込みが完了した瞬間に初めて画像のサイズが分かり、その時点でレイアウトが動いてしまうことがあります。この現象は「レイアウトシフト」と呼ばれ、ユーザーにとって見づらいページの原因となります。
widthとheightをあらかじめ指定しておくことは、このようなレイアウトのガタつきを防ぎ、ユーザー体験を向上させるための重要なテクニックです。
変更点: コードの可読性を高めるためのフォーマット統一
/assets/css/style.css1 /* リセット・基本設定 */ 2 * { 3- box-sizing: border-box; 4- margin: 0; 5- padding: 0; 6+ box-sizing: border-box; 7+ margin: 0; 8+ padding: 0; 9 }
CSSファイルのインデント(字下げ)が統一されました。変更前はスペースがなかったりバラバラだったりしましたが、変更後では一貫して半角スペース4つ分のインデントが適用されています。
このようなコードの見た目を整える作業を「フォーマット」や「整形」と呼びます。
インデントを揃えることは、プログラムの動作に直接影響を与えるわけではありません。しかし、コードの親子関係や構造が視覚的に分かりやすくなるため、他の人が読んだり、未来の自分が修正したりする際に、内容を素早く正確に理解する手助けとなります。
読みやすいコードはバグの発見を容易にし、メンテナンス性を向上させるため、プログラミングにおいて非常に重要な習慣です。
おわりに
今回は、リファクタリングの基本として、CSS変数でサイト全体の色などを一元管理する方法を学びました。また、.containerのように共通のスタイルをクラスにまとめることで、コードの重複をなくし、見通しを良くするテクニックも解説しました。インデントの統一や画像へのサイズ指定といった地道な作業も、コードの品質を高める重要なポイントです。これらの手法を実践し、将来の自分が困らない、管理しやすいコードを書く習慣を身につけていきましょう。