scss-best-practices
保守性とスケーラビリティに優れたスタイルシートを実現するための、SCSS/Sassy CSSのベストプラクティスとコーディングガイドラインを提供します。SCSSの記述品質向上やチームでのコーディング規約整備が必要な場面で活用できます。
description の原文を見る
SCSS/Sassy CSS best practices and coding guidelines for maintainable, scalable stylesheets
SKILL.md 本文
SCSS ベストプラクティス
SCSS(Sassy CSS)、CSS アーキテクチャ、保守性の高いスタイルシート開発の専門家です。
主要な原則
- プロジェクトの複雑さに応じてスケーラブルなモジュール型で再利用可能な SCSS を記述
- 変数、ミックスイン、関数を使用して DRY(Don't Repeat Yourself)原則に従う
- 構造、スキン、状態スタイルの間に明確な分離を保つ
- 巧妙な抽象化よりも可読性と保守性を優先
ファイル構成
アーキテクチャパターン(7-1 パターン)
scss/
├── abstracts/
│ ├── _variables.scss # グローバル変数
│ ├── _functions.scss # SCSS 関数
│ ├── _mixins.scss # 再利用可能なミックスイン
│ └── _placeholders.scss # 拡張可能なプレースホルダ
├── base/
│ ├── _reset.scss # CSS リセット/normalize
│ ├── _typography.scss # タイポグラフィルール
│ └── _base.scss # 基本要素のスタイル
├── components/
│ ├── _buttons.scss # ボタンコンポーネント
│ ├── _cards.scss # カードコンポーネント
│ └── _forms.scss # フォームコンポーネント
├── layout/
│ ├── _header.scss # ヘッダーレイアウト
│ ├── _footer.scss # フッターレイアウト
│ ├── _grid.scss # グリッドシステム
│ └── _navigation.scss # ナビゲーションレイアウト
├── pages/
│ ├── _home.scss # ホームページ固有
│ └── _contact.scss # お問い合わせページ固有
├── themes/
│ └── _default.scss # デフォルトテーマ
├── vendors/
│ └── _bootstrap.scss # サードパーティのオーバーライド
└── main.scss # メインマニフェストファイル
インポート順序
// main.scss
@use 'abstracts/variables';
@use 'abstracts/functions';
@use 'abstracts/mixins';
@use 'abstracts/placeholders';
@use 'vendors/normalize';
@use 'base/reset';
@use 'base/typography';
@use 'base/base';
@use 'layout/grid';
@use 'layout/header';
@use 'layout/navigation';
@use 'layout/footer';
@use 'components/buttons';
@use 'components/cards';
@use 'components/forms';
@use 'pages/home';
@use 'themes/default';
変数
命名規則
// セマンティックで説明的な名前を使用
// フォーマット: $category-property-variant
// 色
$color-primary: #3498db;
$color-primary-light: lighten($color-primary, 15%);
$color-primary-dark: darken($color-primary, 15%);
$color-secondary: #2ecc71;
$color-text: #333333;
$color-text-muted: #666666;
$color-background: #ffffff;
$color-border: #e0e0e0;
$color-error: #e74c3c;
$color-success: #27ae60;
$color-warning: #f39c12;
// タイポグラフィ
$font-family-base: 'Helvetica Neue', Arial, sans-serif;
$font-family-heading: 'Georgia', serif;
$font-size-base: 1rem;
$font-size-small: 0.875rem;
$font-size-large: 1.25rem;
$font-weight-normal: 400;
$font-weight-bold: 700;
$line-height-base: 1.5;
// スペーシング(一貫性のあるスケールを使用)
$spacing-unit: 8px;
$spacing-xs: $spacing-unit * 0.5; // 4px
$spacing-sm: $spacing-unit; // 8px
$spacing-md: $spacing-unit * 2; // 16px
$spacing-lg: $spacing-unit * 3; // 24px
$spacing-xl: $spacing-unit * 4; // 32px
$spacing-xxl: $spacing-unit * 6; // 48px
// ブレークポイント
$breakpoint-sm: 576px;
$breakpoint-md: 768px;
$breakpoint-lg: 992px;
$breakpoint-xl: 1200px;
$breakpoint-xxl: 1400px;
// Z-index スケール
$z-index-dropdown: 1000;
$z-index-sticky: 1020;
$z-index-fixed: 1030;
$z-index-modal-backdrop: 1040;
$z-index-modal: 1050;
$z-index-popover: 1060;
$z-index-tooltip: 1070;
// トランジション
$transition-base: 0.3s ease;
$transition-fast: 0.15s ease;
$transition-slow: 0.5s ease;
// ボーダーラジアス
$border-radius-sm: 2px;
$border-radius-md: 4px;
$border-radius-lg: 8px;
$border-radius-pill: 50px;
$border-radius-circle: 50%;
// シャドウ
$shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
$shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
$shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
$shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.15);
関連値のマップ
// グループ化された値にはマップを使用
$colors: (
'primary': #3498db,
'secondary': #2ecc71,
'danger': #e74c3c,
'warning': #f39c12,
'info': #17a2b8,
'success': #27ae60
);
$breakpoints: (
'sm': 576px,
'md': 768px,
'lg': 992px,
'xl': 1200px,
'xxl': 1400px
);
// map-get でアクセス
.element {
color: map-get($colors, 'primary');
}
ミックスイン
レスポンシブブレークポイント
@mixin respond-to($breakpoint) {
@if map-has-key($breakpoints, $breakpoint) {
@media (min-width: map-get($breakpoints, $breakpoint)) {
@content;
}
} @else {
@warn "Unknown breakpoint: #{$breakpoint}";
}
}
// 使用方法
.element {
width: 100%;
@include respond-to('md') {
width: 50%;
}
@include respond-to('lg') {
width: 33.333%;
}
}
Flexbox ユーティリティ
@mixin flex-center {
display: flex;
align-items: center;
justify-content: center;
}
@mixin flex-between {
display: flex;
align-items: center;
justify-content: space-between;
}
@mixin flex-column {
display: flex;
flex-direction: column;
}
タイポグラフィ
@mixin font-size($size, $line-height: null) {
font-size: $size;
@if $line-height {
line-height: $line-height;
}
}
@mixin truncate($lines: 1) {
@if $lines == 1 {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} @else {
display: -webkit-box;
-webkit-line-clamp: $lines;
-webkit-box-orient: vertical;
overflow: hidden;
}
}
アクセシビリティ
@mixin visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
@mixin focus-visible {
&:focus-visible {
outline: 2px solid $color-primary;
outline-offset: 2px;
}
}
BEM 命名規則
構造
// Block: スタンドアロンコンポーネント
// Element: ブロックの一部 (block__element)
// Modifier: バリアント (block--modifier または block__element--modifier)
.card {
// ブロックのスタイル
background: $color-background;
border-radius: $border-radius-md;
box-shadow: $shadow-md;
// エレメント
&__header {
padding: $spacing-md;
border-bottom: 1px solid $color-border;
}
&__title {
margin: 0;
font-size: $font-size-large;
font-weight: $font-weight-bold;
}
&__body {
padding: $spacing-md;
}
&__footer {
padding: $spacing-md;
border-top: 1px solid $color-border;
}
// モディファイア
&--featured {
border: 2px solid $color-primary;
}
&--compact {
.card__header,
.card__body,
.card__footer {
padding: $spacing-sm;
}
}
}
ネスティングルール
最大ネスティング深度
// 悪い例: ネストが深すぎる
.nav {
.nav-list {
.nav-item {
.nav-link {
.nav-icon {
// 5 レベル深い - 避ける
}
}
}
}
}
// 良い例: ネストを最大 3 レベルに制限
.nav {
// レベル 1
}
.nav__list {
// レベル 1
}
.nav__item {
// レベル 1
}
.nav__link {
color: $color-text;
&:hover,
&:focus {
// レベル 2 - 状態に対しては許容可能
color: $color-primary;
}
&--active {
// レベル 2 - モディファイアに対しては許容可能
color: $color-primary;
font-weight: $font-weight-bold;
}
}
許容可能なネスティング
.component {
// 直下の疑似要素
&::before,
&::after {
content: '';
}
// 状態モディファイア
&:hover,
&:focus,
&:active {
// 状態のスタイル
}
// BEM モディファイア
&--variant {
// モディファイアのスタイル
}
// メディアクエリ
@include respond-to('md') {
// レスポンシブスタイル
}
}
関数
色関数
@function tint($color, $percentage) {
@return mix(white, $color, $percentage);
}
@function shade($color, $percentage) {
@return mix(black, $color, $percentage);
}
// 使用方法
.element {
background: tint($color-primary, 20%);
border-color: shade($color-primary, 10%);
}
単位変換
@function px-to-rem($px, $base: 16) {
@return ($px / $base) * 1rem;
}
@function rem-to-px($rem, $base: 16) {
@return ($rem / 1rem) * $base * 1px;
}
// 使用方法
.element {
font-size: px-to-rem(18); // 1.125rem
padding: px-to-rem(24); // 1.5rem
}
スペーシング関数
@function spacing($multiplier) {
@return $spacing-unit * $multiplier;
}
// 使用方法
.element {
margin-bottom: spacing(2); // 16px
padding: spacing(3); // 24px
}
extend とプレースホルダ
クラスではなくプレースホルダを使用
// プレースホルダを定義
%button-base {
display: inline-flex;
align-items: center;
justify-content: center;
padding: $spacing-sm $spacing-md;
border: none;
border-radius: $border-radius-md;
font-family: inherit;
font-size: $font-size-base;
font-weight: $font-weight-bold;
text-decoration: none;
cursor: pointer;
transition: all $transition-base;
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
// プレースホルダを拡張
.btn-primary {
@extend %button-base;
background: $color-primary;
color: white;
&:hover:not(:disabled) {
background: darken($color-primary, 10%);
}
}
.btn-secondary {
@extend %button-base;
background: transparent;
color: $color-primary;
border: 2px solid $color-primary;
&:hover:not(:disabled) {
background: $color-primary;
color: white;
}
}
ループと反復
ユーティリティクラスの生成
// スペーシングユーティリティ
$spacing-directions: (
'': '',
't': '-top',
'r': '-right',
'b': '-bottom',
'l': '-left',
'x': '-inline',
'y': '-block'
);
@each $abbr, $direction in $spacing-directions {
@for $i from 0 through 8 {
.m#{$abbr}-#{$i} {
margin#{$direction}: spacing($i);
}
.p#{$abbr}-#{$i} {
padding#{$direction}: spacing($i);
}
}
}
// 色ユーティリティ
@each $name, $color in $colors {
.text-#{$name} {
color: $color;
}
.bg-#{$name} {
background-color: $color;
}
.border-#{$name} {
border-color: $color;
}
}
パフォーマンスベストプラクティス
- 過度に詳細なセレクタを避け、詳細度を 0-1-0(単一クラス)を目指す
!importantをユーティリティクラス以外では使用しない- ファイル間の
@extendの使用を最小化(バロー化を引き起こす可能性がある) - 非推奨の
@importの代わりに@useと@forwardを使用 - 開発時はソースマップで、本番環境ではなしでコンパイル
- ベンダープレフィックスは手動ではなく autoprefixer を使用
モダン SCSS 機能
モジュールシステム
// _variables.scss
$primary: #3498db;
// _mixins.scss
@use 'variables' as vars;
@mixin themed-button {
background: vars.$primary;
}
// main.scss
@use 'mixins';
.button {
@include mixins.themed-button;
}
組み込みモジュール
@use 'sass:math';
@use 'sass:color';
@use 'sass:list';
@use 'sass:map';
@use 'sass:string';
.element {
width: math.div(100%, 3);
background: color.adjust($color-primary, $lightness: 10%);
}
コードスタイル
- インデントは 2 スペース使用
- 文字列にはシングルクォートを使用
- 宣言のコロンの後にスペースを追加
- 開き括弧の前にスペースを追加
- 閉じ括弧は新しい行に配置
- ルールセットを空行で区切る
- プロパティを論理的に順序付け(ポジショニング、ボックスモデル、タイポグラフィ、ビジュアル、その他)
- 複雑な計算と明白でないコードにコメントを付ける
ライセンス: Apache-2.0(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- mindrally
- リポジトリ
- mindrally/skills
- ライセンス
- Apache-2.0
- 最終更新
- 不明
Source: https://github.com/mindrally/skills / ライセンス: Apache-2.0
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。