雑学1473627 views
中学英語811754 views
高校物理160216 views
小学算数1200472 views
中学理科1630686 views
Computer368134 views
数学講師2886596 views
高校倫理1440172 views
高校日本史190578 views
中学数学623778 views
Help
Tools
NewsSpreadsheetCalendarBookkeepingMarkdown TablesLanguage Model NewsSlidesTier ListPen ToolIllustrationCrayonWatercolorPixel ArtASCII ArtPerspectiveEndless StairsGraphMind MapER DiagramFamily TreeMemeCurved TextImage EditorMosaicRetro FilterPencil SketchSwirl EffectLine ArtOCR/HighlighterMakeup EditorFaviconVideo TrimmerScrolling VideoVideo TitleColor PickerColor ExtractorBonfireFireworksCherry BlossomWater RippleWater SplashBreaking GlassGlass TextureFabric TextureWood GrainMarble TextureBrick Wall TextureMetal TextureWashi Paper TextureCardboard TextureCSS ButtonIcon MakerBar ChartGrouped Bar ChartStacked Bar ChartPie ChartLine ChartArea ChartStacked Area ChartScatter Plot3D Bar Chart3D Pie ChartBar Chart RaceBubble ChartPopulation PyramidPictogramEarningsCandlestick ChartInvestment RiskMortgage SimulatorCalculatorMatrix CalculatorFunction GraphPolynomial ExpansionVenn DiagramField VisualizerRubik's Cube Group TheoryTraveling SalesmanVoronoi and DelaunayFractalColumn ArithmeticDraw Math FiguresArithmetic AnimationArithmetic Word ProblemsCounting with Tree DiagramsCube NetsRolling DiceCross SectionsMotion PathMechanicsWavesElectromagnetic WavesCapacitorsLight and LensesThermodynamicsHow Semiconductors WorkMolecular StructuresAtomic OrbitalsElectrochemical CellsChemical EquilibriumCrystal LatticesBuffer pHOrganic Reaction MapPeriodic TableComplex IonsDNA Double HelixCell DivisionMembrane ChannelsNerve ImpulseMuscle ContractionHormones and HomeostasisRock ClassificationWeatherConstellationsSolar and Lunar Eclipses3D ModelingFloor PlanSeismic StructuresIntersection TurnMaglevCooking AnimationOrigamiLive Viewer CountGeoJSON MapRailway MapPopulation MapCrime MapLand Price MapSchool MapShrine and Castle MapHouse of Representatives MapWord MapSolitaireReversiHakoiri MusumeChessHamburgerRippleSlide Puzzle MakerNeon PinballNovel MakerJapanese Typing PracticePiano Score EditorMusic TheoryShogi StrategyPiano Rhythm Game

English

マジックナンバーを排除する CSS の書き方

CSS の中に top: -3pxwidth: 347px のような数値が唐突に現れることがあります。この数値がなぜその値なのか、コードからは読み取れません。こうした根拠の不明な数値をマジックナンバーと呼びます。

マジックナンバーの問題

以下のコードを見ただけでは、なぜ 37px なのか分かりません。

.dropdown {
  top: 37px;
}

この 37px はおそらくヘッダーの高さに合わせた値ですが、ヘッダーの高さが変わっても 37px はそのまま残り続けます。修正するときにはヘッダーとドロップダウンの関係を知っている人が、両方のファイルを突き合わせて確認しなければなりません。

マジックナンバーが引き起こす問題は大きく 2 つあります。

変更に弱い

値の根拠が別の場所にあるため、元の値が変わったときに追従できない。修正漏れが起きやすい。

意図が伝わらない

数値だけでは「何に合わせた値なのか」「なぜこの数値なのか」が分からない。レビューでも見落とされやすい。

カスタムプロパティで意味を与える

CSS カスタムプロパティを使えば、数値に名前を付けて根拠を明示できます。

:root {
  --header-height: 37px;
}

.dropdown {
  top: var(--header-height);
}

37px--header-height になったことで、ヘッダーの高さに連動しているという関係が明確です。ヘッダーの高さを変更するときも :root の 1 箇所を直すだけで済みます。

ただし何でもカスタムプロパティにすればいいわけではありません。padding: 16px のような一般的なスペーシングまで全部変数化すると、定義ファイルが肥大化してかえって見通しが悪くなります。

変数化すべき値

他の要素と連動している値、複数箇所で同じ根拠から導かれる値、変更される可能性が高い値。

変数化しなくてよい値

そのコンポーネント固有の値で、他と連動しておらず、意図が文脈から明らかな値。

デザイントークンとしての変数設計

プロジェクト全体のスペーシングやフォントサイズに一貫性を持たせるために、デザイントークンとして変数を体系的に定義する方法があります。

:root {
  --spacing-xs: 4px;
  --spacing-sm: 8px;
  --spacing-md: 16px;
  --spacing-lg: 24px;
  --spacing-xl: 32px;

  --font-size-sm: 12px;
  --font-size-md: 14px;
  --font-size-lg: 16px;
  --font-size-xl: 20px;
}

この体系があれば padding: 13px のような中途半端な値が入り込みにくくなります。13px はトークンのどれにも該当しないので、レビュー時に「なぜこの値なのか」という疑問が自然に発生します。

/* トークンに従った記述 */
.card {
  padding: var(--spacing-md);
  font-size: var(--font-size-md);
}

/* マジックナンバー */
.card {
  padding: 13px;
  font-size: 15px;
}

トークンに存在しない値を使っている箇所は、デザインとの乖離かマジックナンバーのどちらかです。いずれにしても確認が必要なコードだということが、トークン体系があることで機械的に判別できるようになります。

calc() で根拠を式にする

マジックナンバーの中には、複数の値を足し引きした結果として導かれたものがあります。その場合は calc() で計算式をそのまま書くことで、根拠をコードに残せます。

/* マジックナンバー */
.sidebar {
  height: 648px;
}

/* calc() で根拠を明示 */
.sidebar {
  height: calc(100vh - var(--header-height) - var(--footer-height));
}

648px という値がビューポートの高さからヘッダーとフッターを引いた結果だったとしても、数値だけではその関係は読み取れません。calc() で式にしておけば、ヘッダーやフッターの高さが変わっても自動的に追従します。

/* 複数の根拠が絡む例 */
.tooltip {
  /* ボタンの高さ + ボタンとツールチップの間隔 */
  top: calc(var(--button-height) + var(--spacing-sm));
}

calc() の中にカスタムプロパティを入れることで、数値の由来が完全にコード上で説明されます。コメントを書かなくても式自体が意図を語ってくれる形です。

マジックナンバーを見つける方法

既存のコードからマジックナンバーを見つけるには、Stylelint を使えます。

{
  "rules": {
    "declaration-property-value-no-unknown": true
  }
}

ただし Stylelint にはマジックナンバーを直接検出するルールは組み込まれていません。stylelint-declaration-strict-value というプラグインを使うと、特定のプロパティに対して変数やキーワード以外の値を禁止できます。

{
  "plugins": [
    "stylelint-declaration-strict-value"
  ],
  "rules": {
    "scale-unlimited/declaration-strict-value": [
      ["/color$/", "font-size", "z-index"],
      {
        "ignoreValues": ["inherit", "transparent", "currentColor"]
      }
    ]
  }
}

この設定では colorfont-sizez-index に対してリテラル値を直接書くとエラーになります。変数かキーワードのみ許可されるので、マジックナンバーの混入を機械的に防げます。

どこまで徹底するか

マジックナンバーの排除を突き詰めると、すべての数値を変数にしたくなりますが、現実的にはコストとのバランスです。

厳密に排除すべきもの

色、z-index、ブレイクポイント、他の要素と連動するサイズ。これらは変数化しないと保守時に破綻しやすい。

許容してよいもの

border-radius: 4pxgap: 8px のようなコンポーネント内に閉じた値で、デザイントークンに合致しているもの。変数を経由しなくても意図は明らか。

重要なのは「なぜこの値なのか」がコードから読み取れるかどうかです。トークンの体系に沿った値であれば、変数を使わず直接書いても根拠は自明です。トークンに存在しない値、他の要素に依存する値、計算で導かれた値が、マジックナンバーとして排除すべき対象になります。