diff --git a/.editorconfig b/.editorconfig new file mode 100755 index 0000000..2f1404f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +# https://editorconfig.org +root = true +indent_style = tab +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..d14214a --- /dev/null +++ b/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": ["@d-zero/eslint-config/base"] +} diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 0000000..d3f3d01 --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "configMigration": true, + "extends": [ + "config:recommended", + "docker:pinDigests", + "helpers:pinGitHubActionDigests", + ":pinDevDependencies", + ":semanticCommitTypeAll(chore)" + ], + "lockFileMaintenance": { + "enabled": true, + "automerge": true + }, + "autoApprove": true, + "labels": ["Dependencies", "Renovate"], + "packageRules": [ + { + "matchDepTypes": ["optionalDependencies"], + "addLabels": ["Dependencies: Optional"] + }, + { + "matchDepTypes": ["devDependencies"], + "addLabels": ["Dependencies: Development"] + }, + { + "matchDepTypes": ["dependencies"], + "addLabels": ["Dependencies: Production"] + }, + { + "description": "Automerge non-major updates", + "matchUpdateTypes": ["minor", "patch", "pin", "digest"], + "matchCurrentVersion": "!/^0/", + "automerge": true + } + ] +} diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..68c073b --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,44 @@ +name: Deploy Pages + +on: + push: + branches: ['main'] + pull_request: + branches: + - main + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: 'pages' + cancel-in-progress: true + +jobs: + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Use Node.js + uses: actions/setup-node@v1 + with: + node-version: 22 + - name: Install dependencies + run: yarn install + - name: Build Pages + run: yarn build + - name: Setup Pages + uses: actions/configure-pages@v2 + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 + with: + path: './dist' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8d3df81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,79 @@ +# macOS +.DS_Store + +# Dist *.js sources +lib + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# next.js build output +.next + +# TypeScript +*.tsbuildinfo + +# Secret Test +test/fixture/.__* + +# Vitepress +src/.vitepress/cache + +# Pages +.dist diff --git a/.prettierrc.mjs b/.prettierrc.mjs new file mode 100644 index 0000000..bb3531c --- /dev/null +++ b/.prettierrc.mjs @@ -0,0 +1,3 @@ +import config from '@d-zero/prettier-config'; + +export default config; diff --git a/.textlintrc.js b/.textlintrc.js new file mode 100644 index 0000000..9bd208e --- /dev/null +++ b/.textlintrc.js @@ -0,0 +1,9 @@ +module.exports = { + ...require('@d-zero/textlint-config'), + 'no-mix-dearu-desumasu': { + preferInHeader: 'である', + preferInBody: 'ですます', + preferInList: 'である', + strict: true, + }, +}; diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..1d6b1aa --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "dbaeumer.vscode-eslint", + "editorconfig.editorconfig", + "esbenp.prettier-vscode", + "streetsidesoftware.code-spell-checker" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..de6ca8f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,15 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib", + "editor.defaultFormatter": "esbenp.prettier-vscode", + "eslint.validate": ["javascript", "typescript"], + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, + "github.copilot.enable": { + "*": true, + "plaintext": false, + "log": false, + "dotenv": false + } +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0acb854 --- /dev/null +++ b/LICENSE @@ -0,0 +1,4 @@ +Copyright (c) 2024 D-ZERO Co., Ltd. + +Licensed under CC BY-NC-SA 4.0 +https://creativecommons.org/licenses/by-nc-sa/4.0/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..3fe0628 --- /dev/null +++ b/README.md @@ -0,0 +1,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/) diff --git a/cspell.json b/cspell.json new file mode 100644 index 0000000..b0b81f2 --- /dev/null +++ b/cspell.json @@ -0,0 +1,11 @@ +{ + "import": ["@d-zero/cspell-config"], + "words": [ + // + "huskyrc", + "nodist", + "Splide", + "splidejs", + "WCAG" + ] +} diff --git a/lint-staged.config.mjs b/lint-staged.config.mjs new file mode 100644 index 0000000..d0474bd --- /dev/null +++ b/lint-staged.config.mjs @@ -0,0 +1,2 @@ +import lintStagedConfigGenerator from '@d-zero/lint-staged-config'; +export default lintStagedConfigGenerator(); diff --git a/package.json b/package.json new file mode 100644 index 0000000..d72553f --- /dev/null +++ b/package.json @@ -0,0 +1,36 @@ +{ + "name": "@d-zero/frontend-guidelines", + "version": "5.0.0-alpha.0", + "description": "D-ZERO Frontend Developer's Guideline", + "repository": "https://github.com/d-zero-dev/frontend-env.git", + "author": "D-ZERO Co., Ltd.", + "license": "CC BY-NC-SA 4.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vitepress dev src", + "build": "vitepress build src", + "lint": "run-s lint:eslint lint:prettier lint:textlint lint:cspell", + "lint:cspell": "cspell --no-progress --show-suggestions \"{*,src/*}/\"", + "lint:eslint": "eslint --fix \"*.{js,cjs,mjs}\"", + "lint:prettier": "prettier --write \"{*,src/*}.{md,json,js,cjs,mjs,jsx,ts,cts,mts,tsx}\"", + "lint:textlint": "textlint --fix ./src/*.md; textlint ./src/*.md", + "up": "yarn upgrade-interactive --latest" + }, + "devDependencies": { + "@d-zero/cspell-config": "5.0.0-alpha.23", + "@d-zero/eslint-config": "5.0.0-alpha.23", + "@d-zero/lint-staged-config": "5.0.0-alpha.23", + "@d-zero/prettier-config": "5.0.0-alpha.23", + "@d-zero/textlint-config": "5.0.0-alpha.23", + "@d-zero/tsconfig": "0.2.0", + "mermaid": "10.9.0", + "npm-run-all2": "6.1.2", + "vitepress": "1.1.4", + "vitepress-plugin-mermaid": "2.0.16" + }, + "volta": { + "node": "22.1.0", + "yarn": "1.22.22" + } +} diff --git a/src/.vitepress/config.js b/src/.vitepress/config.js new file mode 100644 index 0000000..9960016 --- /dev/null +++ b/src/.vitepress/config.js @@ -0,0 +1,20 @@ +import { defineConfig } from 'vitepress'; +import { withMermaid } from 'vitepress-plugin-mermaid'; + +export default async () => { + return withMermaid( + defineConfig({ + outDir: process.cwd() + '/.dist', + lang: 'ja', + title: 'ディーゼロ コーディングガイドライン', + description: + '株式会社ディーゼロの主にフロントエンド開発のために規定しているコーディンガイドラインです。', + themeConfig: { + footer: { + message: 'Licensed under CC BY-NC-SA 4.0', + copyright: 'Copyright © 2024 D-ZERO Co., Ltd.', + }, + }, + }), + ); +}; diff --git a/src/.vitepress/theme/custom.css b/src/.vitepress/theme/custom.css new file mode 100644 index 0000000..7aa7bce --- /dev/null +++ b/src/.vitepress/theme/custom.css @@ -0,0 +1,23 @@ +.vp-doc div[class*='language-'] + div[class*='language-'], +.vp-doc div[class$='-api'] + div[class*='language-'], +.vp-doc div[class*='language-'] + div[class$='-api'] > div[class*='language-'] { + margin-block-start: 1em; +} + +@media print { + .VPNav { + position: relative !important; + } + + .VPLocalNav { + display: none !important; + } + + .vp-doc [class*='language-'] { + border: 1px solid gray; + } + + .vp-doc [class*='language-'] :is(pre, code) { + white-space: pre-wrap; + } +} diff --git a/src/.vitepress/theme/index.js b/src/.vitepress/theme/index.js new file mode 100644 index 0000000..149273e --- /dev/null +++ b/src/.vitepress/theme/index.js @@ -0,0 +1,4 @@ +import DefaultTheme from 'vitepress/theme'; +import './custom.css'; + +export default DefaultTheme; diff --git a/src/breakpoint.png b/src/breakpoint.png new file mode 100644 index 0000000..65f8879 Binary files /dev/null and b/src/breakpoint.png differ diff --git a/src/css.md b/src/css.md new file mode 100644 index 0000000..85661b7 --- /dev/null +++ b/src/css.md @@ -0,0 +1,721 @@ +# CSS ガイドライン + +🔰 当ドキュメントは「[コーディングガイドライン](./index.md)」の一部です。 +基本的なガイドライン・ルールについては先にそれから確認してください。 + +## 🎯 CSS ガイドラインの目的 + +CSSのガイドラインは次の3つを主な目的として策定されています。 + +- 確実性 + - 影響範囲が明瞭であること + - 確実に場所を特定して追加・変更ができること +- 機能性 + - 命名規則が機能的でその機能が予測し易くあること + - プロジェクトにヘルプアサインされてもほとんどが判断がつくこと +- 一貫性 + - 一貫した汎用ルールがあることでプロジェクト独自ルールの氾濫を避けられること + - プロジェクトをまたいでも混乱することがないこと + +### 作用をきちんと理解する + +「何故か解らないけど出来た」は一番やってはいけません。プロパティひとつひとつ、セレクタひとつひとつが、どういった作用をするかきちんと理解してコーディングすることを心掛けてください。 + +### 別解を用意すること + +ほとんどの場合、表現の方法はひとつではありません。メンテナンス性を優先させた方法、パフォーマンスを優先させた方法など、そのときそのときで最適な方法を探す必要があります。常にいくつかのパターンを考えながらコーディングできるように心掛けてください。また、万が一にブラウザの予期せぬバグに遭遇した際にも別解で解決する必要があります。 + +## 💅 コードスタイル + +*Stylelint*に設定されているルールに則って記述します。 + +::: danger リントエラーについて +例外なく必ずリントエラーを修正してください。**リンターのルールが現状にそぐわない場合はルールの見直しを行ってください**。 +::: + +::: warning Stylelintの`disable`コメント + +`disable`コメントを利用することで、ルールを無視することができますが**原則行わない**でください。 +`_content-main.scss` などのように既に`disable`コメントがある場合もあるがこれは特有の理由があるので削除しないようにしてください。 + +```scss +.c-header { + /* ❌ 実装上やむを得ない場合を除いてdisableコメントは使用しないこと */ + width: 100px !important; // stylelint-disable-line declaration-no-important +} +``` + +::: + +## 🍴 プリプロセッサー・コンパイル環境 + +11tyを介して[Viteの標準のCSS変換](https://ja.vitejs.dev/guide/features#css)を行います。プリプロセッサーはSASSを利用します。 + +```mermaid +flowchart LR + #in["*.scss"] + #out["*.css"] + #vite(["Vite"]) + + #in --> #dzBuilder --> #out + + subgraph #dzBuilder["@d-zero/builder"] + direction LR + + subgraph #11ty["11ty"] + subgraph #vite["vite"] + direction TB + #sass --> #postcss + end + end + end +``` + +### ベンダープレフィックス + +Autoprefixerを利用するのでベンダープレフィックス付きのプロパティは必要ありません。Stylelintによってベンダープレフィックス付きのプロパティは警告されます。 + +```scss +selector { + transition: opacity 300ms; + -webkit-transition: opacity 300ms; // ❌ 不要 + -moz-transition: opacity 300ms; // ❌ 不要 +} +``` + +ただしCSSの標準規格でないものについては必要なケースがあります。Stylelintはその点を考慮して警告を出すので心配はありません。 + +```scss +selector { + -moz-osx-font-smoothing: grayscale; // ✅ ブラウザ固有のプロパティのためプレフィックは必要 + -webkit-font-smoothing: antialiased; // ✅ ブラウザ固有のプロパティのためプレフィックは必要 +} +``` + +## 📂 ファイル構成 + +ファイルは以下の構成で管理します。 + +``` +# リソース管理 +📂 __assets/ +├── 📂 htdocs/ +│ └── 📂 theme/ +│ ├── style.scss +│ └── bge_style.scss +└── 📂 _libs/ + ├── 📂 component/ + │ ├── _component-name-a.scss + │ ︙ + │ └── _component-name-z.scss + └── 📂 style/ + ├── 📂 theme/ + │ ├── _index.scss + │ ├── _color.scss + │ ├── _demension.scss + │ └── _font.scss + ├── 📂 base/ + │ ├── _reset.scss + │ └── _root.scss + └── 📂 general/ + ├── _tag-name-a.scss + ︙ + └── _tag-name-z.scss + +# 公開ファイル +📂 htdocs/css/ +├── style.css +└── bge_style.css +``` + +`__assets/htdocs`フォルダの内容は**フォルダ構造をそのまま**にドキュメントルートの`htdocs`にCSSファイルとしてコンパイルされ出力されます。 + +SASSの機能に従って`@use`する断片ファイルは`_`で始まるファイル名とします。 + +### `__assets/htdocs/css/style.scss` + +`style.css`にコンパイルするSCSSファイルです。`@use`を利用して各断片ファイルをインポートし、ここにスタイルは定義しないようにしてください。 + +`base` → `general` → `component` の順にカスケードされるようにインポートしてください。 + +```scss +/* useの例 */ +@use 'base/reset'; +@use 'base/root'; +@use 'general/tag-name-a'; +@use 'general/tag-name-z'; +@use 'component/component-name-a'; +@use 'component/component-name-z'; +``` + +### `__assets/_libs/style/theme/` + +カラー・数値など、**SASS変数**やミックスインを定義します。 + +- `_color.scss`: カラーコードのSASS変数の定義 +- `_demension.scss`: 全体のレイアウトの寸法・マージン・パディングのSASS変数の定義 +- `_font.scss`: `@font-face`・フォントファミリーのSASS変数の定義、アイコンのコードポイントの変数・アイコン出力用のミックスインの定義 + +**カスタムプロパティは`base/_root.scss`などのスコープに応じたファイルに定義してください。** + +::: tip 他のSCSSファイル上での変数の利用 + +`@use '../theme' as *;`を記述することで`theme/_index.scss`を参照し`theme/`フォルダ内で定義した変数やミックスインを利用することができます。 + +```scss +@use '../theme' as *; // `_demension.scss`に`$root-font-size`が定義されているとする + +selector { + font-size: $root-font-size; +} +``` + +::: + +::: danger プレースホルダーの禁止 + +カスケード(定義の順番)が期待通りにしにくい問題があるので**プレースホルダー**を`__assets/_libs/style/theme/`内に定義するは原則禁止とします。 + +```scss +// ❌ プレースホルダーの利用は原則禁止 +%any-style { + any-property: any-value; +} + +// ✅ ミックスインを利用する +@mixin anyMixin { + any-property: any-value; +} +``` + +::: + +::: warning ミックスインの利用時の注意 + +タグやクラスに依存するミックスインは作らないようにしてください。影響範囲を予測できなくなるため`@mixin`のスコープ内に子孫セレクタをつくらないようにしてください。 + +```scss +// ❌ 特定のタグでしか利用できないミックスインは作らない +@mixin anyList { + display: flex; + + // ❌ ミックインのスコープ内に子孫セレクタをつくらない + li { + flex: 0 1 auto; + } +} +``` + +::: + +### `__assets/_libs/style/base/_reset.scss` + +リセット用のスタイルを定義します。デフォルトでは[Normalize.css](https://necolas.github.io/normalize.css/)を利用しています。 + +```scss +@import 'normalize.css' layer(reset); +``` + +カスケードレイヤーは`reset`として定義します。 + +### `__assets/_libs/style/base/_root.scss` + +ルート要素に対するスタイル定義を定義します。セレクタは`:root`だけで、他のセレクタを含めないようにしてください。 + +::: tip カスタムプロパティの定義 + +グローバルスコープのカスタムプロパティを定義する場合は、`:root`セレクタ内で行なってください。 + +::: + +### `__assets/_libs/style/general/` + +クラスやIDの付かない素の要素に対してスタイルを定義します。ファイル名はタグ名(要素名)となります。セレクタは当然タイプセレクタのみとなります。 + +- 例) `
` → `_body.scss` +- 例) `` → `_a.scss` + +コンポーネントをまたいだ各要素、つまりページ全体に影響があることに注意してください。そのため必要最小限の定義に留めることを心掛けてください。コンポーネントで定義できるものはコンポーネント内で定義してください。 + +全要素対象の場合は`_all.scss`ファイルに`*`(全称セレクタ)で定義します。 + +```scss +// _all.scssの例 +* { + &, + &::before, + &::after { + box-sizing: border-box; + } +} +``` + +また、定義をしてよい理由は主に以下に限定する。 + +- サイト全体で共通すると断定できる場合。 **ただし、ほとんどの場合、その判断は失敗に終わるので推奨しない。** +- CMS などから入力された要素を、セレクターで判定できない場合(判定できない構造は、HTML と CSS の設計を見直す方を優先する) + +### `__assets/_libs/style/component/` + +要素はコンポーネント単位に分割して管理する。(👉[HTML ガイドライン > コンポーネント](./html.md#component)) +ファイル名はコンポーネント名とする。 **ひとつのファイルの中に複数のコンポーネントを定義してはいけない**。 + +- 例) `