├── .editorconfig ├── .github ├── renovate.json └── workflows │ ├── static.yml │ └── test.yml ├── .gitignore ├── .prettierrc.mjs ├── .textlintrc.js ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── cspell.json ├── eslint.config.js ├── lint-staged.config.mjs ├── package.json ├── src ├── .vitepress │ ├── config.js │ └── theme │ │ ├── custom.css │ │ └── index.js ├── browsers.md ├── contributing.md ├── css │ ├── builder.md │ ├── ids.md │ ├── index.md │ ├── order.md │ ├── rules.md │ ├── selectors.md │ ├── structure.md │ ├── style.md │ ├── values.md │ └── variables.md ├── environment.md ├── git │ ├── branches.md │ ├── hooks.md │ ├── index.md │ └── workflow.md ├── html │ ├── accessibility.md │ ├── builder.md │ ├── components.md │ ├── doctype.md │ ├── elements.md │ ├── ids.md │ ├── index.md │ ├── interactions.md │ ├── links.md │ ├── meta.md │ ├── structure.md │ └── style.md ├── img │ └── breakpoint.png ├── index.md ├── js │ ├── builder.md │ ├── development.md │ ├── index.md │ ├── interactions.md │ ├── libraries.md │ ├── loading.md │ ├── no-style-attr.md │ ├── performance.md │ ├── structure.md │ └── style.md ├── media │ ├── audio.md │ ├── font.md │ ├── image.md │ ├── index.md │ └── video.md ├── naming │ ├── abbreviation.md │ ├── consistency.md │ ├── index.md │ ├── principles.md │ ├── spelling.md │ └── structure.md ├── preparation.md ├── public │ └── favicon.png ├── roles.md ├── rules.md ├── testing.md ├── text-files.md ├── tools │ └── setup-volta.md └── versioning.md └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | root = true 3 | indent_style = tab 4 | indent_size = 2 5 | 6 | [*.md] 7 | trim_trailing_whitespace = false 8 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "configMigration": true, 4 | "extends": [ 5 | "config:recommended", 6 | "docker:pinDigests", 7 | "helpers:pinGitHubActionDigests", 8 | ":pinDevDependencies", 9 | ":semanticCommitTypeAll(chore)" 10 | ], 11 | "lockFileMaintenance": { 12 | "enabled": true, 13 | "automerge": true 14 | }, 15 | "autoApprove": true, 16 | "labels": ["Dependencies", "Renovate"], 17 | "packageRules": [ 18 | { 19 | "matchDepTypes": ["optionalDependencies"], 20 | "addLabels": ["Dependencies: Optional"] 21 | }, 22 | { 23 | "matchDepTypes": ["devDependencies"], 24 | "addLabels": ["Dependencies: Development"] 25 | }, 26 | { 27 | "matchDepTypes": ["dependencies"], 28 | "addLabels": ["Dependencies: Production"] 29 | }, 30 | { 31 | "description": "Automerge non-major updates", 32 | "matchUpdateTypes": ["minor", "patch", "pin", "digest"], 33 | "matchCurrentVersion": "!/^0/", 34 | "automerge": true 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /.github/workflows/static.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Pages 2 | 3 | on: 4 | push: 5 | branches: ['main'] 6 | 7 | permissions: 8 | contents: read 9 | pages: write 10 | id-token: write 11 | 12 | concurrency: 13 | group: 'pages' 14 | cancel-in-progress: true 15 | 16 | jobs: 17 | # Single deploy job since we're just deploying 18 | deploy: 19 | environment: 20 | name: github-pages 21 | url: ${{ steps.deployment.outputs.page_url }} 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: Checkout 25 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 26 | - name: Use Node.js 27 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 28 | with: 29 | node-version: 22 30 | - name: Install dependencies 31 | run: yarn install 32 | - name: Build Pages 33 | run: yarn build 34 | - name: Setup Pages 35 | uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5 36 | - name: Upload artifact 37 | uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3 38 | with: 39 | # Upload entire repository 40 | path: '.dist' 41 | - name: Deploy to GitHub Pages 42 | id: deployment 43 | uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 44 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - dev 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 14 | 15 | - name: Setup Node.js 16 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 17 | with: 18 | node-version: 22 19 | - name: Cache dependencies 20 | id: cache-depends 21 | uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4 22 | with: 23 | path: '**/node_modules' 24 | key: depends-${{ hashFiles('yarn.lock') }} 25 | - name: Install dependencies 26 | if: steps.cache-depends.outputs.cache-hit != 'true' 27 | run: yarn --frozen-lockfile --check-files 28 | - name: Lint 29 | run: yarn lint 30 | - name: Build 31 | run: yarn build 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # macOS 2 | .DS_Store 3 | 4 | # Dist *.js sources 5 | lib 6 | 7 | # Logs 8 | logs 9 | *.log 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Runtime data 15 | pids 16 | *.pid 17 | *.seed 18 | *.pid.lock 19 | 20 | # Directory for instrumented libs generated by jscoverage/JSCover 21 | lib-cov 22 | 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # TypeScript v1 declaration files 46 | typings/ 47 | 48 | # Optional npm cache directory 49 | .npm 50 | 51 | # Optional eslint cache 52 | .eslintcache 53 | 54 | # Optional REPL history 55 | .node_repl_history 56 | 57 | # Output of 'npm pack' 58 | *.tgz 59 | 60 | # Yarn Integrity file 61 | .yarn-integrity 62 | 63 | # dotenv environment variables file 64 | .env 65 | 66 | # next.js build output 67 | .next 68 | 69 | # TypeScript 70 | *.tsbuildinfo 71 | 72 | # Secret Test 73 | test/fixture/.__* 74 | 75 | # Vitepress 76 | src/.vitepress/cache 77 | 78 | # Pages 79 | .dist 80 | -------------------------------------------------------------------------------- /.prettierrc.mjs: -------------------------------------------------------------------------------- 1 | export { default } from '@d-zero/prettier-config'; 2 | -------------------------------------------------------------------------------- /.textlintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ...require('@d-zero/textlint-config'), 3 | 'no-mix-dearu-desumasu': { 4 | preferInHeader: 'である', 5 | preferInBody: 'ですます', 6 | preferInList: 'である', 7 | strict: true, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "editorconfig.editorconfig", 5 | "esbenp.prettier-vscode", 6 | "streetsidesoftware.code-spell-checker" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "editor.defaultFormatter": "esbenp.prettier-vscode", 4 | "eslint.validate": ["javascript", "typescript"], 5 | "editor.formatOnSave": true, 6 | "editor.codeActionsOnSave": { 7 | "source.fixAll.eslint": "explicit" 8 | }, 9 | "github.copilot.enable": { 10 | "*": true, 11 | "plaintext": false, 12 | "log": false, 13 | "dotenv": false, 14 | "markdown": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 D-ZERO Co., Ltd. 2 | 3 | Licensed under CC BY-NC-SA 4.0 4 | https://creativecommons.org/licenses/by-nc-sa/4.0/ 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ディーゼロ フロントエンドガイドライン 2 | 3 | 株式会社ディーゼロの主にフロントエンド開発のために規定しているガイドラインです。 4 | 5 | Copyright (c) 2024 D-ZERO Co., Ltd. Licensed under [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) 6 | -------------------------------------------------------------------------------- /cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": ["@d-zero/cspell-config"], 3 | "ignorePaths": [".vscode/*"], 4 | "words": [ 5 | // 6 | "huskyrc", 7 | "nodist", 8 | "Splide", 9 | "splidejs", 10 | "vitepress", 11 | "WCAG" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import dz from '@d-zero/eslint-config'; 2 | 3 | /** 4 | * @type {import('eslint').Linter.Config[]} 5 | */ 6 | export default [ 7 | ...dz.configs.standard, 8 | { 9 | files: ['.textlintrc.js'], 10 | ...dz.configs.commonjs, 11 | }, 12 | ]; 13 | -------------------------------------------------------------------------------- /lint-staged.config.mjs: -------------------------------------------------------------------------------- 1 | import lintStagedConfigGenerator from '@d-zero/lint-staged-config'; 2 | export default lintStagedConfigGenerator(); 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@d-zero/frontend-guidelines", 3 | "version": "5.0.0-alpha.0", 4 | "description": "D-ZERO Frontend Developer's Guideline", 5 | "repository": "https://github.com/d-zero-dev/frontend-guidelines.git", 6 | "author": "D-ZERO Co., Ltd.", 7 | "license": "CC BY-NC-SA 4.0", 8 | "private": true, 9 | "type": "module", 10 | "scripts": { 11 | "dev": "vitepress dev src", 12 | "build": "vitepress build src", 13 | "lint": "run-s lint:eslint lint:prettier lint:textlint lint:cspell", 14 | "lint:cspell": "cspell --no-progress --show-suggestions \"{*,src/{**,.vitepress/**}/*}/\"", 15 | "lint:eslint": "eslint --fix \"*.{js,cjs,mjs}\"", 16 | "lint:prettier": "prettier --write \"{*,src/{**,.vitepress/**}/*}.{md,json,js,cjs,mjs,jsx,ts,cts,mts,tsx}\"", 17 | "lint:textlint": "textlint --fix ./src/{**,.vitepress/**}/*.md; textlint ./src/{**,.vitepress/**}/*.md", 18 | "up": "yarn upgrade-interactive --latest" 19 | }, 20 | "devDependencies": { 21 | "@d-zero/cspell-config": "5.0.0-alpha.63", 22 | "@d-zero/eslint-config": "5.0.0-alpha.63", 23 | "@d-zero/lint-staged-config": "5.0.0-alpha.63", 24 | "@d-zero/prettier-config": "5.0.0-alpha.63", 25 | "@d-zero/textlint-config": "5.0.0-alpha.63", 26 | "@d-zero/tsconfig": "0.4.1", 27 | "mermaid": "11.6.0", 28 | "npm-run-all2": "8.0.4", 29 | "typescript": "5.8.3", 30 | "vitepress": "1.6.3", 31 | "vitepress-plugin-mermaid": "2.0.17" 32 | }, 33 | "volta": { 34 | "node": "22.16.0", 35 | "yarn": "1.22.22" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/.vitepress/config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitepress'; 2 | import { withMermaid } from 'vitepress-plugin-mermaid'; 3 | 4 | export default async () => { 5 | return withMermaid( 6 | defineConfig({ 7 | outDir: process.cwd() + '/.dist', 8 | lang: 'ja', 9 | title: 'D-ZERO Guidelines', 10 | description: 11 | '株式会社ディーゼロの主にフロントエンド開発のために規定しているガイドラインです。', 12 | head: [['link', { rel: 'icon', type: 'image/png', href: '/favicon.png' }]], 13 | themeConfig: { 14 | search: { 15 | provider: 'local', 16 | }, 17 | nav: [ 18 | { 19 | text: 'コーディングガイドライン', 20 | link: '/', 21 | }, 22 | { 23 | text: 'コンポーネント集', 24 | link: 'https://components.d-zero.co.jp/', 25 | target: '_blank', 26 | }, 27 | { 28 | text: 'GitHub', 29 | link: 'https://github.com/d-zero-dev', 30 | target: '_blank', 31 | }, 32 | ], 33 | sidebar: [ 34 | { 35 | text: '🚩 コーディングガイドライン', 36 | link: '/', 37 | collapsed: true, 38 | items: [ 39 | { text: 'ガイドラインの更新', link: '/contributing' }, 40 | { text: '制作に取り掛かる前に', link: '/preparation' }, 41 | { text: '対応ブラウザとデバイス', link: '/browsers' }, 42 | { text: 'プロジェクトのルールの最適化', link: '/rules' }, 43 | { text: '責任者の選任', link: '/roles' }, 44 | { text: '制作環境', link: '/environment' }, 45 | { text: 'バージョン管理', link: '/versioning' }, 46 | { text: 'テキストファイルに関して', link: '/text-files' }, 47 | { text: '検証とテスト', link: '/testing' }, 48 | ], 49 | }, 50 | { 51 | text: '🛤 Gitガイドライン', 52 | link: '/git/', 53 | collapsed: true, 54 | items: [ 55 | { text: 'ブランチとテストサイト', link: '/git/branches' }, 56 | { text: 'pre-commitフック', link: '/git/hooks' }, 57 | { text: 'Git操作手順', link: '/git/workflow' }, 58 | ], 59 | }, 60 | { 61 | text: '📛 識別子の命名規則', 62 | link: '/naming/', 63 | collapsed: true, 64 | items: [ 65 | { text: '命名方針', link: '/naming/principles' }, 66 | { text: 'スペルチェック', link: '/naming/spelling' }, 67 | { text: '文字構成', link: '/naming/structure' }, 68 | { text: '省略', link: '/naming/abbreviation' }, 69 | { text: '識別子の統一', link: '/naming/consistency' }, 70 | ], 71 | }, 72 | { 73 | text: '📗 HTMLガイドライン', 74 | link: '/html/', 75 | collapsed: true, 76 | items: [ 77 | { text: 'コードスタイル', link: '/html/style' }, 78 | { text: 'ファイル構成', link: '/html/structure' }, 79 | { text: 'ビルド環境', link: '/html/builder' }, 80 | { text: 'DOCTYPE', link: '/html/doctype' }, 81 | { text: 'メタ要素', link: '/html/meta' }, 82 | { text: 'パスとリンク', link: '/html/links' }, 83 | { text: 'コンポーネント', link: '/html/components' }, 84 | { 85 | text: 'メインコンテンツのエレメントとヘルパークラス', 86 | link: '/html/elements', 87 | }, 88 | { text: 'id属性の利用', link: '/html/ids' }, 89 | { text: 'セマンティックとアクセシビリティ', link: '/html/accessibility' }, 90 | { text: 'インタラクションの実装', link: '/html/interactions' }, 91 | ], 92 | }, 93 | { 94 | text: '📘 CSSガイドライン', 95 | link: '/css/', 96 | collapsed: true, 97 | items: [ 98 | { text: 'コードスタイル', link: '/css/style' }, 99 | { text: 'ビルド環境', link: '/css/builder' }, 100 | { text: 'ファイル構成', link: '/css/structure' }, 101 | { text: 'IDセレクタの利用の禁止', link: '/css/ids' }, 102 | { text: 'ルールの定義規則', link: '/css/rules' }, 103 | { text: 'タイプセレクタの利用', link: '/css/selectors' }, 104 | { text: '記述順番', link: '/css/order' }, 105 | { 106 | text: 'カスタムプロパティ・カスタムクエリー・変数・関数・ミックスイン・プレースホルダー', 107 | link: '/css/variables', 108 | }, 109 | { text: '値のルール', link: '/css/values' }, 110 | ], 111 | }, 112 | { 113 | text: '🖼️ 画像・メディアリソースガイドライン', 114 | link: '/media/', 115 | collapsed: true, 116 | items: [ 117 | { text: '画像', link: '/media/image' }, 118 | { text: '動画', link: '/media/video' }, 119 | { text: '音声', link: '/media/audio' }, 120 | { text: 'フォント', link: '/media/font' }, 121 | ], 122 | }, 123 | { 124 | text: '📙 JavaScriptガイドライン', 125 | link: '/js/', 126 | collapsed: true, 127 | items: [ 128 | { text: 'コードスタイル', link: '/js/style' }, 129 | { text: 'ビルド環境', link: '/js/builder' }, 130 | { text: 'ファイル構成', link: '/js/structure' }, 131 | { text: 'HTMLへの読み込み', link: '/js/loading' }, 132 | { text: '開発ファイル', link: '/js/development' }, 133 | { text: 'インタラクションの実装', link: '/js/interactions' }, 134 | { text: 'ライブラリ・モジュールの利用', link: '/js/libraries' }, 135 | { text: 'style属性をなるべく変更しない', link: '/js/no-style-attr' }, 136 | { text: 'パフォーマンスを意識した実装', link: '/js/performance' }, 137 | ], 138 | }, 139 | ], 140 | footer: { 141 | message: 'Licensed under CC BY-NC-SA 4.0', 142 | copyright: 'Copyright © 2024 D-ZERO Co., Ltd.', 143 | }, 144 | editLink: { 145 | pattern: 146 | 'https://github.com/d-zero-dev/frontend-guidelines/edit/main/src/:path', 147 | text: 'GitHubで編集する', 148 | }, 149 | lastUpdated: { 150 | text: '最終更新日', 151 | formatOptions: { 152 | dateStyle: 'long', 153 | }, 154 | }, 155 | }, 156 | }), 157 | ); 158 | }; 159 | -------------------------------------------------------------------------------- /src/.vitepress/theme/custom.css: -------------------------------------------------------------------------------- 1 | .VPNav { 2 | a.title { 3 | text-wrap: auto; 4 | } 5 | } 6 | 7 | .vp-doc div[class*='language-'] + div[class*='language-'], 8 | .vp-doc div[class$='-api'] + div[class*='language-'], 9 | .vp-doc div[class*='language-'] + div[class$='-api'] > div[class*='language-'] { 10 | margin-block-start: 1em; 11 | } 12 | 13 | @media print { 14 | .VPNav { 15 | position: relative !important; 16 | } 17 | 18 | .VPLocalNav { 19 | display: none !important; 20 | } 21 | 22 | .vp-doc [class*='language-'] { 23 | border: 1px solid gray; 24 | } 25 | 26 | .vp-doc [class*='language-'] :is(pre, code) { 27 | white-space: pre-wrap; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/.vitepress/theme/index.js: -------------------------------------------------------------------------------- 1 | import DefaultTheme from 'vitepress/theme'; 2 | import './custom.css'; 3 | 4 | export default DefaultTheme; 5 | -------------------------------------------------------------------------------- /src/browsers.md: -------------------------------------------------------------------------------- 1 | # 対応ブラウザとデバイス 2 | 3 | - Windows Edge / Chrome / Firefox: いずれもリリース時点での最新版 4 | - macOS Chrome / Firefox: いずれもリリース時点での最新版 5 | - macOS Safari : リリース時点での最新版とその 1 つ前のマイナーバージョン 6 | - Android Chrome: リリース時点での最新版 7 | - iOS Safari : リリース時点での最新版とその 1 つ前のマイナーバージョン 8 | 9 | ::: danger レガシーブラウザへの対応 10 | 上記以外のバージョンおよびレガシーブラウザへの対応は案件に合わせて対応しても構いません。 11 | ただし原則として**追加費用**が発生するため、クライアントとの契約に基づいて対応してください。 12 | ::: 13 | -------------------------------------------------------------------------------- /src/contributing.md: -------------------------------------------------------------------------------- 1 | # ガイドラインの更新 2 | 3 | このガイドラインは日々更新されるウェブの技術やトレンドによって最適化されるべきです。 4 | 変更・追記・削除に関しては常に議論されることが期待されます。 5 | 集めた意見や議論の結果は、リードエンジニアのもとで更新するか判断されます。 6 | プルリクエストを送ることもでき、その場合もリードエンジニアの判断によりマージされます。 7 | -------------------------------------------------------------------------------- /src/css/builder.md: -------------------------------------------------------------------------------- 1 | # ビルド環境 2 | 3 | *11ty*のTransform機能から[*Vite*のCSS変換](https://ja.vitejs.dev/guide/features#css)を利用します。プリプロセッサーはSassを利用します。 4 | 5 | ```mermaid 6 | flowchart LR 7 | #in["*.scss"] 8 | #out["*.css"] 9 | #vite(["Vite"]) 10 | 11 | #in --> #dzBuilder --> #out 12 | 13 | subgraph #dzBuilder["@d-zero/builder"] 14 | direction LR 15 | 16 | subgraph #11ty["11ty"] 17 | subgraph #vite["vite"] 18 | direction TB 19 | #sass --> #postcss 20 | end 21 | end 22 | end 23 | ``` 24 | 25 | ## ベンダープレフィックス 26 | 27 | *Autoprefixer*を利用するのでベンダープレフィックス付きのプロパティは必要ありません。 28 | 29 | ```scss 30 | selector { 31 | transition: opacity 300ms; 32 | -webkit-transition: opacity 300ms; // ❌ 不要 33 | -moz-transition: opacity 300ms; // ❌ 不要 34 | } 35 | ``` 36 | 37 | ただしCSSの標準規格でないものについては必要なケースがあります。*Stylelint*はその点を考慮して警告を出すので心配はありません。 38 | 39 | ```scss 40 | selector { 41 | -moz-osx-font-smoothing: grayscale; // ✅ ブラウザ固有のプロパティのためプレフィックは必要 42 | -webkit-font-smoothing: antialiased; // ✅ ブラウザ固有のプロパティのためプレフィックは必要 43 | } 44 | ``` 45 | 46 | ::: tip 🔧 自動修正可能 47 | このルールは*Stylelint*によって自動修正されます。 48 | ::: 49 | -------------------------------------------------------------------------------- /src/css/ids.md: -------------------------------------------------------------------------------- 1 | # IDセレクタの利用の禁止 2 | 3 | 詳細度で問題を起こすのでIDをセレクタとしてスタイルを定義しないでください。 4 | -------------------------------------------------------------------------------- /src/css/index.md: -------------------------------------------------------------------------------- 1 | # CSS ガイドライン 2 | 3 | 🔰 当ドキュメントは「[コーディングガイドライン](../index.md)」の一部です。 4 | 基本的なガイドライン・ルールについては先にそれから確認してください。 5 | 6 | ## CSS ガイドラインの目的 7 | 8 | CSSのガイドラインは次の3つを主な目的として策定されています。 9 | 10 | - 確実性 11 | - 影響範囲が明瞭であること 12 | - 確実に場所を特定して追加・変更ができること 13 | - 機能性 14 | - 命名規則が機能的でその機能が予測し易くあること 15 | - プロジェクトにヘルプアサインされてもほとんどが判断がつくこと 16 | - 一貫性 17 | - 一貫した汎用ルールがあることでプロジェクト独自ルールの氾濫を避けられること 18 | - プロジェクトをまたいでも混乱することがないこと 19 | 20 | ### 作用をきちんと理解する 21 | 22 | 「何故か解らないけど出来た」は一番やってはいけません。プロパティひとつひとつ、セレクタひとつひとつが、どういった作用をするかきちんと理解してコーディングすることを心掛けてください。 23 | 24 | ### 別解を用意すること 25 | 26 | ほとんどの場合、表現の方法はひとつではありません。メンテナンス性を優先させた方法、パフォーマンスを優先させた方法など、そのときそのときで最適な方法を探す必要があります。常にいくつかのパターンを考えながらコーディングできるように心掛けてください。また、万が一にブラウザの予期せぬバグに遭遇した際にも別解で解決する必要があります。 27 | -------------------------------------------------------------------------------- /src/css/order.md: -------------------------------------------------------------------------------- 1 | # 記述順番 2 | 3 | 以下のような順番で定義します。 4 | 5 | ```scss 6 | .component-name { 7 | // 1. 変数定義 8 | // 2. コンポーネント自体のスタイル 9 | // 3. 疑似要素 10 | // 4. 状態変化(※) 11 | // 4-1. メディアクエリ 12 | // 4-2. 疑似クラス(:hover :disabled :nth-child :empty など) 13 | // 4-3. 属性 14 | // 4-4. 状態クラス (&--[状態]) 15 | // 5. エレメント 16 | // 6. 結合子セレクタを利用したエレメント(E+E E~E など) 17 | // 7. 子孫要素に影響のある状態変化 18 | } 19 | /* EOF */ 20 | ``` 21 | 22 | また、プロパティについては*Stylelint*の設定に基づいて種類順に記述します。 23 | 24 | ::: tip 👮‍♀️ 自動検知 25 | このルールは*Stylelint*によって警告されます。 26 | ::: 27 | 28 | ::: danger クラス名の例外 29 | 30 | JavaScript のライブラリの利用など、クラス命名規則に当てはまらないセレクタにスタイルを当てないといけない場合があります。その場合は、`.stylelintrc`ファイルにて`selector-class-pattern`を変更してください。 31 | 32 | ```scss 33 | .c-hero { 34 | // ⚠️ 通常はStylelintによる警告がでる 35 | .any-js-lib-class-name { 36 | /* declaration */ 37 | } 38 | } 39 | 40 | .c-hero { 41 | // ✅ .stylelintrcの設定変更によって警告がなくなる 42 | .any-js-lib-class-name { 43 | /* declaration */ 44 | } 45 | } 46 | ``` 47 | 48 | ::: 49 | -------------------------------------------------------------------------------- /src/css/rules.md: -------------------------------------------------------------------------------- 1 | # ルールの定義規則 2 | 3 | headerコンポーネントの場合を例に解説します。 4 | 5 | 1階層目のセレクタはコンポーネントの定義となり、SCSSファイル内に一度だけ登場するようにします。 6 | ファイルパスは `__assets/_libs/style/component/c-header.scss` となります。 7 | 8 | ```scss 9 | .c-header { 10 | /* declaration */ 11 | } 12 | /* EOF */ 13 | ``` 14 | 15 | 次にエレメントは、それにネストする形で`&`を利用したセレクタをつくります。 16 | 17 | ```scss 18 | .c-header { 19 | /* declaration */ 20 | 21 | &__body { 22 | /* declaration */ 23 | } 24 | 25 | &__title { 26 | /* declaration */ 27 | } 28 | 29 | &__site-name { 30 | /* declaration */ 31 | } 32 | } 33 | /* EOF */ 34 | ``` 35 | 36 | こうすることで、このファイルに記述されたスタイルの影響範囲が、コンポーネント内であることを保証します。 37 | 38 | 状態変化を表す場合も`&`を利用してネストして定義します。メディアクエリの定義も、ユーザエージェントの状態変化と捉えて同様にネストして定義します。 39 | 40 | 状態変化がエレメントを巻き込む場合はコードを後ろにまわして記述します。また、`&`がコンポーネントのクラスを表さないようなネストの状態になる場合があるので変数化して定義します。 41 | 42 | 43 | 44 | ```scss 45 | .c-header { 46 | --foo-bar: 0; // カスタムプロパティ 47 | --foo-baz: calc(32 / 16 * 1em); // カスタムプロパティ 48 | 49 | /* declaration */ 50 | 51 | @media (--sm-lte) { /* declaration */ } // メディアクエリ 52 | &:hover { /* declaration */ } // 疑似クラス 53 | &--compact-mode { /* declaration */ } // 状態クラス 54 | &[data-compact-mode="true"] { /* declaration */ } // data属性 55 | &[aria-hidden="true"] { /* declaration */ } // aria属性 56 | 57 | &__body { 58 | /* declaration */ 59 | 60 | // 子孫要素も同様のルールになる 61 | @media (--sm-lte) { /* declaration */ } // メディアクエリ 62 | &:hover { /* declaration */ } // 疑似クラス 63 | &--compact-mode { /* declaration */ } // 状態クラス 64 | &[data-compact-mode="true"] { /* declaration */ } // data属性 65 | &[aria-hidden="true"] { /* declaration */ } // aria属性 66 | } 67 | 68 | // 影響がエレメントを巻き込む場合は、後ろに記述する 69 | &[data-fat-mode="true"] { 70 | /* declaration */ 71 | 72 | .c-header__body { // ⚠️ `&` が使用できないスコープでは直接クラスを記述する 73 | /* declaration */ 74 | } 75 | } 76 | } 77 | /* EOF */ 78 | ``` 79 | 80 | 81 | 疑似要素は子孫要素の前に定義し、これも`&`を利用する。疑似要素セレクタは`::`で定義してください。 82 | 83 | 84 | ```scss 85 | .c-header { 86 | /* declaration */ 87 | 88 | &[data-compact-mode="true"] { /* declaration */ } 89 | 90 | &::before { /* declaration */ } 91 | &::after { /* declaration */ } 92 | 93 | &__body { 94 | /* declaration */ 95 | } 96 | } 97 | /* EOF */ 98 | ``` 99 | 100 | 101 | ::: warning `&`でクラス名を連結する是非について 102 | フルのクラス名が検索にヒットしない理由から忌避されることがありますが、コンポーネントのクラス名とファイル名が一致していることを前提にしているため、`&`を利用してネストして記述するルールを採用しています。 103 | ::: 104 | 105 | ::: tip 👮‍♀️ 自動検知 106 | このルールは*Stylelint*によって警告されます。 107 | ::: 108 | -------------------------------------------------------------------------------- /src/css/selectors.md: -------------------------------------------------------------------------------- 1 | # タイプセレクタの利用 2 | 3 | ::: danger 重要 4 | 5 | タイプセレクタの利用は可能ですが**コンポーネントはコンポーネントを内包できる**点に注意してください。 6 | 7 | ::: 8 | 9 | ```scss 10 | .c-header { 11 | /* declaration */ 12 | 13 | ul { 14 | /* declaration */ 15 | } 16 | li { 17 | /* declaration */ 18 | } 19 | } 20 | ``` 21 | 22 | ```html 23 |
24 | 28 |
29 | 35 |
36 |
37 | ``` 38 | 39 | 回避方法としては次の方法を検討してください。 40 | 41 | - エレメントにクラスきちんと付けて対象を限定する 42 | - `>`結合子を利用して影響範囲を限定する(ただしHTMLの構造変更に弱いのでメンテナンス性が落ちることに注意が必要です) 43 | -------------------------------------------------------------------------------- /src/css/structure.md: -------------------------------------------------------------------------------- 1 | # ファイル構成 2 | 3 | ファイルは以下の構成で管理します。 4 | 5 | ``` 6 | # リソース管理 7 | 📂 __assets/ 8 | ├── 📂 htdocs/css/ 9 | │ ├── style.scss 10 | │ └── bge_style.scss 11 | └── 📂 _libs/ 12 | ├── 📂 component/ 13 | │ ├── c-component-name-a.scss 14 | │ ︙ 15 | │ └── c-component-name-z.scss 16 | └── 📂 style/ 17 | ├── 📂 base/ 18 | │ └── root.scss 19 | └── 📂 general/ 20 | ├── all.scss 21 | ├── body.scss 22 | ├── button.scss 23 | ├── tag-name-a.scss 24 | ︙ 25 | └── tag-name-z.scss 26 | 27 | # 公開ファイル 28 | 📂 htdocs/css/ 29 | ├── style.css 30 | └── bge_style.css 31 | ``` 32 | 33 | `__assets/htdocs`フォルダの内容は**フォルダ構造をそのまま**にドキュメントルートの`htdocs`にCSSファイルとしてコンパイルされ出力されます。 34 | 35 | ## `__assets/htdocs/css/style.scss` 36 | 37 | `style.css`にコンパイルするSCSSファイルです。`@import`を利用して各断片ファイルをインポートし、ここにスタイルは定義しないようにしてください。`@import`は***Vite*のCSS変換**によりインライン化されます。パスは`@`で始めることにより`__assets/_libs`フォルダをルートとして指定します(ビルド設定によっては変わります)。 38 | 39 | CSSレイヤーを利用してインポートするファイルを分類します。 40 | 41 | ```scss 42 | @import 'destyle.css' layer(reset); 43 | 44 | @layer base { 45 | @import '@/style/base/root.scss'; 46 | } 47 | 48 | @layer general { 49 | @import '@/style/general/all.scss'; 50 | @import '@/style/general/body.scss'; 51 | @import '@/style/general/button.scss'; 52 | @import '@/style/general/img.scss'; 53 | } 54 | 55 | @layer components { 56 | @import '@/component/c-page-home.scss'; 57 | @import '@/component/c-page-sub.scss'; 58 | @import '@/component/c-header.scss'; 59 | @import '@/component/c-footer.scss'; 60 | @import '@/component/c-nav-global.scss'; 61 | @import '@/component/c-nav-breadcrumb.scss'; 62 | @import '@/component/c-title-page.scss'; 63 | @import '@/component/c-pagination.scss'; 64 | @import '@/component/c-content-main.scss'; 65 | } 66 | 67 | @layer reset, base, general, components; 68 | ``` 69 | 70 | ::: danger プレースホルダーの禁止 71 | 72 | カスケード(定義の順番)が期待通りにしにくい問題があるので**プレースホルダー**を`__assets/_libs/style/theme/`内に定義するは原則禁止とします。 73 | 74 | ```scss 75 | // ❌ プレースホルダーの利用は原則禁止 76 | %any-style { 77 | any-property: any-value; 78 | } 79 | 80 | // ✅ ミックスインを利用する 81 | @mixin anyMixin { 82 | any-property: any-value; 83 | } 84 | ``` 85 | 86 | ::: tip 👮‍♀️ 自動検知 87 | このルールは*Stylelint*によって警告されます。 88 | ::: 89 | 90 | ::: warning ミックスインの利用時の注意 91 | 92 | タグやクラスに依存するミックスインは作らないようにしてください。影響範囲を予測できなくなるため`@mixin`のスコープ内に子孫セレクタをつくらないようにしてください。 93 | 94 | ```scss 95 | // ❌ 特定のタグでしか利用できないミックスインは作らない 96 | @mixin anyList { 97 | display: flex; 98 | 99 | // ❌ ミックインのスコープ内に子孫セレクタをつくらない 100 | li { 101 | flex: 0 1 auto; 102 | } 103 | } 104 | ``` 105 | 106 | ::: 107 | 108 | ## `__assets/_libs/style/base/root.scss` 109 | 110 | ルート要素に対するスタイル定義を定義します。セレクタは`:root`だけで、他のセレクタを含めないようにしてください。 111 | 112 | グローバルスコープのカスタムプロパティを定義する場合は、`:root`セレクタ内で行なってください。**Figmaで定義されたバリアブルスはここに定義します**。 113 | 114 | ```scss 115 | :root { 116 | --lightest-color: #fff; 117 | --darkest-color: #333; 118 | 119 | // 配色 120 | --base-font-color: var(--darkest-color); 121 | --border-color: var(--darkest-color); 122 | 123 | // タイポグラフィ 124 | --base-font-size: 16px; 125 | --base-line-height: 1.8; 126 | } 127 | ``` 128 | 129 | ## `__assets/_libs/style/general/` 130 | 131 | クラスやIDの付かない素の要素に対してスタイルを定義します。ファイル名はタグ名(要素名)となります。セレクタは当然タイプセレクタのみとなります。 132 | 133 | - 例) `` → `body.scss` 134 | - 例) `` → `a.scss` 135 | 136 | コンポーネントをまたいだ各要素、つまりページ全体に影響があることに注意してください。そのため必要最小限の定義に留めることを心掛けてください。コンポーネントで定義できるものはコンポーネント内で定義してください。 137 | 138 | 全要素対象の場合は`all.scss`ファイルに`*`(全称セレクタ)で定義します。 139 | 140 | ```scss 141 | // all.scssの例 142 | * { 143 | &, 144 | &::before, 145 | &::after { 146 | box-sizing: border-box; 147 | } 148 | } 149 | ``` 150 | 151 | また、定義をしてよい理由は主に以下に限定します。 152 | 153 | - サイト全体で共通すると断定できる場合。 **ただし、ほとんどの場合、その判断は失敗に終わるので推奨しないでください。** 154 | - CMSなどから入力された要素を、セレクターで判定できない場合(判定できない構造は、HTMLとCSSの設計を見直す方を優先します) 155 | 156 | ## `__assets/_libs/style/component/` 157 | 158 | 要素はコンポーネント単位に分割して管理します。(👉[HTMLガイドライン > コンポーネント](../html/components.md)) 159 | ファイル名はコンポーネント名とします。 **ひとつのファイルの中に複数のコンポーネントを定義しないでください**。 160 | 161 | - 例) `
` → `c-header-page.scss` 162 | - 例) `