lism.config.js でのカスタマイズ
プロジェクトのルート直下に lism.config.js(または lism.config.ts / lism.config.mjs)を置き、次のセットアップを行うと、コンポーネントが受け付ける props / tokens / traits やブレイクポイントを拡張できます。
lism.config.js を使うには @lism-css/plugin が必要です。Vite / Astro では統合プラグインでコンポーネントと CSS の両方に自動反映されます。Next.js では withLism() を使います。それ以外の構成では CSS だけを同梱の CLI でビルドします(統合プラグインを使わない構成で反映する)。
セットアップ
@lism-css/plugin の統合プラグイン lismCss() を、Vite / Astro の設定ファイルに追加します。これ1つで、lism.config.js の読み込み・CSS への自動反映・型定義 lism-env.d.ts の自動生成がまとめて有効になります。
lism.config.js を読み込むのはこのプラグインです。設定ファイルに追加していないと、lism.config.js を置いても内容は使われません。
pnpm add -D @lism-css/pluginimport { defineConfig } from 'astro/config';import { lismCss } from '@lism-css/plugin/astro';
export default defineConfig({ integrations: [lismCss()],});import { defineConfig } from 'vite';import { lismCss } from '@lism-css/plugin/vite';
export default defineConfig({ plugins: [lismCss()],});Next.js では統合プラグインの代わりに、@lism-css/plugin/next の withLism() を使います。セットアップ手順はインストールの「Next.js での導入」を参照してください。
プラグインはプロジェクトルートから lism.config.ts → lism.config.mjs → lism.config.js の順で自動検出します。別の場所のファイルを使う場合は configPath オプションでパスを指定できます。
integrations: [lismCss({ configPath: './config/lism.config.js' })],設定ファイルの書き方
フォーマット
export default { props: { hoge: { ... }, foo: { ... }, ... }, tokens: { hoge: { ... }, foo: { ... }, ... }, traits: { isHoge: 'is--hoge', setFoo: 'set--foo', ... }, // breakpoints と isFullMode も同じファイルで指定します(後述)};/**
* isVar: 1 → クラス出力はせずstyle属性での変数出力のみ (--bdw, --keycolor など)
* bp: 0(= bp 省略時のデフォルト)→ Prop-valユーティリティクラス化されなければ、style属性で出力するだけ。
* bp: 1 → .-prop と --prop の セットがベースにあり、.-prop_bp と .--prop_bp で ブレイクポイント指定できる。
* .-prop{property:var(--prop)} が基本で、ユーティリティクラスは .-prop:val{property:value} となる。
*
* ↓コンポーネント処理で使用される
* tokenClass: 1 → 対応するトークン値がそのまま全てユーティリティクラス化されるもの。
* shorthands: → コンポーネント側で短く書くための設定
*
* ↓SCSS出力で使用される
* alwaysVar: 1 → state変数扱い。 .-prop,[class*=-prop:] {property:var(--prop)} の base 出力となり、
* ユーティリティクラスは --prop をセットする形になる。
* 加えて BPクラスも .-prop_$bp { property: var(--prop); --prop: var(--prop_$bp) !important; } を出力し、
* 常に --prop が当該要素の現在値になるよう上書きされる(consumer が --prop を参照できる)。
* important: 1 → !important を付けて最終的に出力する
*/
const PLACE_PRESETS = ['start', 'center', 'end'] as const;
const PLACE_FX_PRESETS = ['flex-start', 'flex-end'] as const;
const PLACE_SHORTHANDS = { s: 'start', e: 'end', c: 'center', fs: 'flex-start', fe: 'flex-end' } as const;
export default {
f: { prop: 'font', presets: ['inherit'] },
fz: { prop: 'fontSize', token: 'fz', tokenClass: 1, bp: 1 },
fw: {
prop: 'fontWeight',
token: 'fw',
tokenClass: 1,
presets: ['100', '200', '300', '400', '500', '600', '700', '800', '900'],
},
ff: { prop: 'fontFamily', token: 'ff', tokenClass: 1 },
fs: { prop: 'fontStyle', presets: ['italic'], shorthands: { i: 'italic' } },
// 実 line-height(unitless比率)を --lh で管理する。既定の行間管理は hl(half-leading)。
// トークン値も任意数値も --lh 経由で出力する(素の line-height 出力だと * ルールに阻まれ子要素へ継承されないため)。
lh: {
prop: 'lineHeight',
token: 'lh',
tokenClass: 1,
utils: { '1': '1' },
alwaysVar: 1,
},
hl: {
prop: '--hl',
isVar: 1,
token: 'hl',
tokenClass: 1,
bp: 1,
// hl="0" で half-leading なし (--hl:0px) を表現できるようにするユーティリティ。
utils: { '0': '0px' },
},
lts: { prop: 'letterSpacing', token: 'lts', tokenClass: 1 },
ta: { prop: 'textAlign', presets: ['center', 'left', 'right'] },
td: { prop: 'textDecoration', utils: { none: 'none' } },
tt: {
prop: 'textTransform',
presets: ['uppercase', 'lowercase'],
// 旧クラス名 -tt:upper / -tt:lower との互換。props の upper / lower を -tt:uppercase / -tt:lowercase に解決する。
shorthands: { upper: 'uppercase', lower: 'lowercase' },
},
// te: { prop: 'textEmphasis', presets: ['filled'] },
// tsh: { prop: 'textShadow' },
d: {
prop: 'display',
presets: ['none', 'block', 'flex', 'inline-flex', 'grid', 'inline-grid', 'inline', 'inline-block'],
bp: 1,
},
o: { prop: 'opacity', presets: ['0'], token: 'o', tokenClass: 1 },
v: { prop: 'visibility', presets: ['hidden'] },
ov: { prop: 'overflow', presets: ['hidden', 'auto', 'clip'] },
'ov-x': { prop: 'overflowX', presets: ['clip', 'auto', 'scroll'] },
'ov-y': { prop: 'overflowY', presets: ['clip', 'auto', 'scroll'] },
// overflow-clip-margin → safariで使えない
ar: {
prop: 'aspectRatio',
presets: ['21/9', '16/9', '3/2', '1/1'], // 4/3, 2/1
token: 'ar',
tokenClass: 1,
bp: 1,
},
// size
w: { prop: 'width', utils: { fit: 'fit-content' }, presets: ['100%'], token: 'sz', bp: 1 },
h: { prop: 'height', utils: { fit: 'fit-content' }, presets: ['100%'], token: 'sz', bp: 1 },
'min-w': { prop: 'minWidth', presets: ['100%'], token: 'sz', bp: 1 },
'max-w': { prop: 'maxWidth', presets: ['100%'], token: 'sz', bp: 1 },
'min-h': { prop: 'minHeight', presets: ['100%'], token: 'sz', bp: 1 },
'max-h': { prop: 'maxHeight', presets: ['100%'], token: 'sz', bp: 1 },
contentSize: { isVar: 1, presets: ['s', 'm', 'l', 'xl'], token: 'sz' },
sz: { prop: 'inlineSize', token: 'sz', bp: 1 },
'min-sz': { prop: 'minInlineSize', token: 'sz', bp: 1 },
'max-sz': {
prop: 'maxInlineSize',
token: 'sz',
tokenClass: 1,
// full / bleed は inline-size / margin-inline も書き換える複合ルール(_size.scss)なので、BP 値(--max-sz_{bp})としては使えない。
bp: 1,
presets: ['full', 'bleed'],
exUtility: {
full: '',
bleed: '',
},
},
bsz: { prop: 'blockSize', token: 'sz' },
'min-bsz': { prop: 'minBlockSize', token: 'sz' },
'max-bsz': { prop: 'maxBlockSize', token: 'sz' },
// bg
bg: { prop: 'background' },
bgi: { prop: 'backgroundImage' },
bgr: { prop: 'backgroundRepeat', presets: ['no-repeat'] },
bgp: { prop: 'backgroundPosition', presets: ['center'] },
bgsz: { prop: 'backgroundSize', presets: ['cover', 'contain'] },
// bga: { prop: 'backgroundAttachment' }, // fixed
// bgo: { prop: 'backgroundOrigin' }, // border, padding, content
// bgblend: { prop: 'backgroundBlendMode' },
// bgclip: {
// prop: 'backgroundClip',
// presets: ['text'],
// },
bgc: {
prop: 'backgroundColor',
// keycolor は palette カタログ経由で var(--keycolor) に解決される(#479)。
presets: ['base', 'base-2', 'text', 'brand', 'accent', 'keycolor', 'inherit', 'transparent'],
// bdc と同様に currentColor を短く書けるようにする(-bgc:current)。
utils: { current: 'currentColor' },
token: 'color',
exUtility: { inherit: { 'background-color': 'inherit' } },
alwaysVar: 1,
},
c: {
// Note: bg系(bgclip)より後にくるように。
prop: 'color',
presets: ['base', 'text', 'text-2', 'brand', 'accent', 'keycolor', 'inherit'],
token: 'color',
exUtility: {
inherit: { color: 'inherit' }, // --c ではなく color で出力したい
// mix: {'--_c1:currentColor;--_c2:transparent;--c:color-mix(in srgb, var(--_c1) var(--_mix-c, 50%), var(--_c2))'},
},
alwaysVar: 1,
},
keycolor: { isVar: 1, token: 'color' },
bd: { prop: 'border', presets: ['none'] },
bds: { isVar: 1, presets: ['dashed', 'dotted', 'double'] },
bdc: {
isVar: 1,
presets: ['brand', 'accent', 'divider', 'keycolor', 'inherit', 'transparent'],
utils: { current: 'currentColor' },
token: 'color',
},
bdw: { isVar: 1, bp: 1 }, // --bdw のみ
'bd-x': { prop: 'borderInline' },
'bd-y': { prop: 'borderBlock' },
'bd-s': { prop: 'borderInlineStart' },
'bd-e': { prop: 'borderInlineEnd' },
'bd-bs': { prop: 'borderBlockStart' },
'bd-be': { prop: 'borderBlockEnd' },
'bd-t': { prop: 'borderTop' },
'bd-b': { prop: 'borderBottom' },
'bd-l': { prop: 'borderLeft' },
'bd-r': { prop: 'borderRight' },
bdrs: {
prop: 'borderRadius',
presets: ['0'],
token: 'bdrs',
tokenClass: 1,
bp: 1,
alwaysVar: 1,
},
'bdrs-tl': { prop: 'borderTopLeftRadius', token: 'bdrs' },
'bdrs-tr': { prop: 'borderTopRightRadius', token: 'bdrs' },
'bdrs-br': { prop: 'borderBottomRightRadius', token: 'bdrs' },
'bdrs-bl': { prop: 'borderBottomLeftRadius', token: 'bdrs' },
'bdrs-ss': { prop: 'borderStartStartRadius', token: 'bdrs' },
'bdrs-se': { prop: 'borderStartEndRadius', token: 'bdrs' },
'bdrs-es': { prop: 'borderEndStartRadius', token: 'bdrs' },
'bdrs-ee': { prop: 'borderEndEndRadius', token: 'bdrs' },
bxsh: { prop: 'boxShadow', utils: { 0: 'none' }, token: 'bxsh', tokenClass: 1, bp: 1 },
// position
pos: {
prop: 'position',
presets: ['static', 'fixed', 'sticky', 'relative', 'absolute'],
bp: 1,
},
z: { prop: 'zIndex', presets: ['-1', '0', '1', '99'] },
t: { prop: 'top', utils: { 0: '0%' }, presets: ['50%', '100%'], token: 'space' },
l: { prop: 'left', utils: { 0: '0%' }, presets: ['50%', '100%'], token: 'space' },
r: { prop: 'right', utils: { 0: '0%' }, presets: ['50%', '100%'], token: 'space' },
b: { prop: 'bottom', utils: { 0: '0%' }, presets: ['50%', '100%'], token: 'space' },
i: { prop: 'inset', utils: { 0: '0%' }, token: 'space' },
'i-x': { prop: 'insetInline', token: 'space' },
'i-y': { prop: 'insetBlock', token: 'space' },
'i-s': { prop: 'insetInlineStart', token: 'space' },
'i-e': { prop: 'insetInlineEnd', token: 'space' },
'i-bs': { prop: 'insetBlockStart', token: 'space' },
'i-be': { prop: 'insetBlockEnd', token: 'space' },
// space
p: {
prop: 'padding',
presets: ['0'],
token: 'space',
tokenClass: 1,
alwaysVar: 1,
bp: 1,
},
px: { prop: 'paddingInline', presets: ['0'], token: 'space', tokenClass: 1, bp: 1 },
py: { prop: 'paddingBlock', presets: ['0'], token: 'space', tokenClass: 1, bp: 1 },
ps: { prop: 'paddingInlineStart', token: 'space', tokenClass: 1, bp: 1 },
pe: { prop: 'paddingInlineEnd', token: 'space', tokenClass: 1, bp: 1 },
pbs: { prop: 'paddingBlockStart', token: 'space', tokenClass: 1, bp: 1 },
pbe: { prop: 'paddingBlockEnd', token: 'space', tokenClass: 1, bp: 1 },
pl: { prop: 'paddingLeft', token: 'space', tokenClass: 1, bp: 1 },
pr: { prop: 'paddingRight', token: 'space', tokenClass: 1, bp: 1 },
pt: { prop: 'paddingTop', token: 'space', tokenClass: 1, bp: 1 },
pb: { prop: 'paddingBottom', token: 'space', tokenClass: 1, bp: 1 },
m: {
prop: 'margin',
presets: ['auto', '0'],
token: 'space',
tokenClass: 1,
alwaysVar: 1,
bp: 1,
},
mx: { prop: 'marginInline', presets: ['auto', '0'], token: 'space', tokenClass: 1, bp: 1 },
my: { prop: 'marginBlock', presets: ['auto', '0'], token: 'space', tokenClass: 1, bp: 1 },
ms: { prop: 'marginInlineStart', presets: ['auto'], token: 'space', tokenClass: 1, bp: 1 },
me: { prop: 'marginInlineEnd', presets: ['auto'], token: 'space', tokenClass: 1, bp: 1 },
mbs: { prop: 'marginBlockStart', presets: ['auto', '0'], token: 'space', tokenClass: 1, bp: 1 },
mbe: { prop: 'marginBlockEnd', presets: ['auto'], token: 'space', tokenClass: 1, bp: 1 },
ml: { prop: 'marginLeft', token: 'space', tokenClass: 1, bp: 1 },
mr: { prop: 'marginRight', token: 'space', tokenClass: 1, bp: 1 },
mt: { prop: 'marginTop', token: 'space', tokenClass: 1, bp: 1 },
mb: { prop: 'marginBottom', token: 'space', tokenClass: 1, bp: 1 },
g: {
prop: 'gap',
presets: ['0', 'inherit'],
exUtility: { inherit: { gap: 'inherit' } },
token: 'space',
tokenClass: 1,
bp: 1,
},
cg: { prop: 'columnGap', token: 'space', tokenClass: 1, bp: 1 },
rg: { prop: 'rowGap', token: 'space', tokenClass: 1, bp: 1 },
cols: { isVar: 1, bp: 1 },
rows: { isVar: 1, bp: 1 },
// flex
fxf: { prop: 'flexFlow' },
fxw: { prop: 'flexWrap', presets: ['wrap'], bp: 1 },
fxd: { prop: 'flexDirection', presets: ['column', 'column-reverse', 'row-reverse'], bp: 1 },
fx: { prop: 'flex', presets: ['1'], bp: 1 },
fxg: { prop: 'flexGrow', presets: ['1'] },
fxsh: { prop: 'flexShrink', presets: ['0'] },
fxb: { prop: 'flexBasis', bp: 1 },
// grid
// gd: { prop: 'grid' },
gt: {
prop: 'gridTemplate',
bp: 1,
},
gta: { prop: 'gridTemplateAreas', bp: 1 },
gtc: {
prop: 'gridTemplateColumns',
presets: ['subgrid'],
bp: 1,
},
gtr: {
prop: 'gridTemplateRows',
presets: ['subgrid'],
bp: 1,
},
gaf: { prop: 'gridAutoFlow', presets: ['row', 'column'], bp: 1 }, //dense
gac: { prop: 'gridAutoColumns' },
gar: { prop: 'gridAutoRows' },
// grid item
ga: { prop: 'gridArea', utils: { '1/1': '1 / 1' }, bp: 1 },
gc: { prop: 'gridColumn', utils: { '1/-1': '1 / -1' }, bp: 1 },
gr: { prop: 'gridRow', utils: { '1/-1': '1 / -1' }, bp: 1 },
gcs: { prop: 'gridColumnStart' },
gce: { prop: 'gridColumnEnd' },
grs: { prop: 'gridRowStart' },
gre: { prop: 'gridRowEnd' },
// places
// -(ai|ac|ji|jc|aslf|jslf): / -$1:
ai: {
prop: 'alignItems',
presets: [...PLACE_PRESETS, 'stretch', ...PLACE_FX_PRESETS],
shorthands: PLACE_SHORTHANDS,
bp: 1,
},
ac: {
prop: 'alignContent',
presets: [...PLACE_PRESETS, ...PLACE_FX_PRESETS],
utils: { between: 'space-between' },
shorthands: PLACE_SHORTHANDS,
bp: 1,
},
ji: {
prop: 'justifyItems',
presets: [...PLACE_PRESETS, 'stretch', ...PLACE_FX_PRESETS],
shorthands: PLACE_SHORTHANDS,
bp: 1,
},
jc: {
prop: 'justifyContent',
presets: [...PLACE_PRESETS, ...PLACE_FX_PRESETS],
utils: { between: 'space-between' },
shorthands: PLACE_SHORTHANDS,
bp: 1,
},
pi: { prop: 'placeItems', presets: PLACE_PRESETS },
pc: { prop: 'placeContent', presets: PLACE_PRESETS },
aslf: {
prop: 'alignSelf',
presets: [...PLACE_PRESETS, 'stretch'],
shorthands: PLACE_SHORTHANDS,
},
jslf: {
prop: 'justifySelf',
presets: [...PLACE_PRESETS, 'stretch'],
shorthands: PLACE_SHORTHANDS,
},
pslf: { prop: 'placeSelf', presets: PLACE_PRESETS },
order: { prop: 'order', presets: ['0', '-1', '1'], bp: 1 },
// transform
// translate: {
// prop: 'translate',
// utils: {
// '-50X': '-50% 0',
// '-50Y': '0 -50%',
// '-50XY': '-50% -50%',
// },
// },
// rotate: {
// prop: 'rotate',
// utils: {
// [`45`]: '45deg',
// '-45': '-45deg',
// [`90`]: '90deg',
// '-90': '-90deg',
// // '180': '180deg',
// },
// },
// scale: {
// prop: 'scale',
// utils: {
// '-X': '-1 1',
// '-Y': '1 -1',
// '-XY': '-1 -1',
// },
// },
// others
ovw: { prop: 'overflowWrap', presets: ['anywhere'] },
whs: { prop: 'whiteSpace', presets: ['nowrap'] },
// wordbreak: { prop: 'wordBreak', utils: { keep: 'keep-all', all: 'break-all' } },
float: { prop: 'float', presets: ['left', 'right'] },
clear: { prop: 'clear', presets: ['both'] },
iso: { prop: 'isolation', presets: ['isolate'] },
wm: { prop: 'writingMode', presets: ['vertical-rl'], bp: 1 },
} as const; /**
* デザイントークン(キー: 値)。
*
* - キーの順序がそのままカタログ順になる(Object.keys)。
* ユーティリティ生成・prop 受理・型導出はすべてこのキー集合から導出される。
* - 値はビルド時に生成 SCSS(`base/tokens/_tokens.gen.scss`)へ出力される。
* - 値 `'-'` は「カタログに登録するがcssの出力はしない」もの。実値はscssで手書き。
*/
export default {
// 構造変数: 他トークンの計算式から参照される、出力専用のグループ。
// - props からは参照されない(ユーティリティ生成・prop 受理の対象外)。
// - キーがそのまま CSS 変数名になる([[token-var-prefix]] の空プレフィックス)。
// - lism.config の tokens.vars で既存キーの値を上書きする用途(新規キーの追加は想定しない)。
vars: {
// パレットカラーの基準の明度・彩度(赤基準。各色は palette 側の計算式で微調整)
'--L': '60%',
'--C': '0.2',
// フォントサイズ倍音列の分母(7~ に対応)
'--fz-mol': '8',
// ハーフレディングの計算単位(≒ 2px)。
'--hl-unit': '0.125rem',
// 余白の計算単位(≒ 8px)。
'--s-unit': '0.5rem',
},
// セマンティックカラー
color: {
// base: 背景色
base: 'hsl(220 0% 99%)',
'base-2': 'hsl(220 4% 95%)',
// text: コンテンツの文字色
text: 'hsl(220 0% 8%)',
'text-2': 'hsl(220 4% 32%)',
// divider: 境界線の色
divider: 'hsl(220 4% 88%)',
link: 'oklch(50% 0.3 240)', // ≒ hsl(220, 90%, 48%)
brand: 'hsl(212, 88%, 50%)',
accent: 'hsl(347, 65%, 57%)',
// ライトモード・ダークモードのどちらでもブレンドして使えるようなニュートラルカラー。
// Memo: 黒からの変化の方がわかりづらいため、明るめにする。
neutral: 'hsl(220, 2%, 75%)',
// shadow: 影の色。--shc(手書き SCSS)はこの変数の別名で、.set--bxsh から上書きされる。
shadow: 'hsl(220 2% 4% / 8%)',
},
// パレットカラー: 基準の明度 --L / 彩度 --C(vars グループの構造変数)を色相ごとに微調整して算出する。
// keycolor は :root に実値を持たないカタログ専用キー(prop 側で style 属性へ出力する)。
palette: {
red: 'oklch(var(--L) var(--C) 20)',
blue: 'oklch(calc(var(--L) - 4%) calc(var(--C) + 0.01) 264)',
green: 'oklch(calc(var(--L) + 4%) calc(var(--C) - 0.02) 152)',
yellow: 'oklch(calc(var(--L) + 12%) calc(var(--C) - 0.02) 80)',
purple: 'oklch(calc(var(--L) - 4%) calc(var(--C) + 0.01) 288)',
orange: 'oklch(calc(var(--L) + 6%) calc(var(--C) - 0.01) 48)',
pink: 'oklch(calc(var(--L) + 2%) calc(var(--C) + 0.01) 352)',
gray: 'oklch(calc(var(--L) - 4%) calc(var(--C) / 10) 240)',
white: '#fff',
black: '#000',
keycolor: '-',
},
// font-size: 倍音列でのスケーリング(--fz-mol は vars グループの構造変数)。基準は --fz--base。
fz: {
base: '1rem',
'5xl': 'calc(1em * var(--fz-mol) / (var(--fz-mol) - 6))',
'4xl': 'calc(1em * var(--fz-mol) / (var(--fz-mol) - 5))',
'3xl': 'calc(1em * var(--fz-mol) / (var(--fz-mol) - 4))',
'2xl': 'calc(1em * var(--fz-mol) / (var(--fz-mol) - 3))',
xl: 'calc(1em * var(--fz-mol) / (var(--fz-mol) - 2))',
l: 'calc(1em * var(--fz-mol) / (var(--fz-mol) - 1))',
m: '1em',
s: 'calc(1em * var(--fz-mol) / (var(--fz-mol) + 1))',
xs: 'calc(1em * var(--fz-mol) / (var(--fz-mol) + 2))',
'2xs': 'calc(1em * var(--fz-mol) / (var(--fz-mol) + 3))',
},
// font-family
ff: {
/* Base:
* -apple-system/BlinkMacSystemFont → Macで英数字をSan Franciscoに。(前者がSafari/Firefox、後者がChrome用)
* 'Hiragino Sans' → Macでの和文フォントの指定。ここを省くと、Chromeで sans-serifが Hiragino Kaku Gothic ProN になってしまう
* sans-serif: Mac=Hiragino系 / Win=Noto|Meiryo
*
* Note: system-ui は和文に游ゴシックが当たるため使わない。
*/
base: "-apple-system, 'BlinkMacSystemFont', 'Hiragino Sans', sans-serif",
// Accent: 装飾用セリフ。初期状態はあくまで一例で実際はカスタマイズしてもらう想定。
accent: 'Georgia, serif',
// Mono: ui-monospace=各OSのUI等幅 / SFMono・Menlo→Mac・Consolas→Win のフォールバック
mono: "ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace",
},
// font-weight
fw: { light: '300', normal: '400', bold: '600' },
// half-leading: --hl-unit は vars グループの構造変数。
hl: {
base: 'calc(var(--hl-unit) * 3)',
xs: 'var(--hl-unit)',
s: 'calc(var(--hl-unit) * 2)',
l: 'calc(var(--hl-unit) * 4)',
xl: 'calc(var(--hl-unit) * 5)',
},
// line-height: unitless比率。fz に比例した行送りを維持したい場合に使う(既定の行間管理は hl)。
// hl と違い body 等で base 値としてセットしないため、中央キーは base ではなく m。
lh: { xs: '1.125', s: '1.25', m: '1.5', l: '1.75', xl: '2' },
// letter-spacing: xs〜xl は base からの相対値。base は calc() に入るため長さで指定する(normal 不可)。
// Memo: var() は :root で置換されるため、base の上書きは :root で行う(子孫要素での上書きは xs〜xl に伝わらない)。
lts: {
base: '0em',
xs: 'calc(var(--lts--base) - 0.05em)',
s: 'calc(var(--lts--base) - 0.025em)',
l: 'calc(var(--lts--base) + 0.05em)',
xl: 'calc(var(--lts--base) + 0.1em)',
},
// opacity(音楽の強弱記号 piano 系列に由来)
o: { mp: '0.9', p: '0.8', pp: '0.6', ppp: '0.4' },
// +0.125 → +0.25 → +0.375 → + 0.5 と 二階等差数列で増えつつ、10~40がフィボナッチ数列。inner は .set--bdrsInner で計算
bdrs: { '10': '0.25rem', '20': '0.375rem', '30': '0.625rem', '40': '1rem', '50': '1.5rem', '99': '99rem', inner: '-' },
// box-shadow: 構造変数 --shsz--*(手書き SCSS)と影色 --shc を合成。.set--bxsh で再宣言され影色 --shc を上書きできる。
// Memo: --shc は color.shadow(--shadow)の別名(手書き SCSS で定義)。
bxsh: {
'10': 'var(--shsz--5) var(--shc--near), var(--shsz--10) var(--shc)',
'20': 'var(--shsz--10) var(--shc--near), var(--shsz--20) var(--shc)',
'30': 'var(--shsz--20) var(--shc--near), var(--shsz--30) var(--shc)',
'40': 'var(--shsz--30) var(--shc--near), var(--shsz--40) var(--shc)',
'50': 'var(--shsz--40) var(--shc--near), var(--shsz--50) var(--shc)',
},
// aspect-ratio
ar: { og: '1.91/1' },
// space: 構造変数 --s-unit(vars グループ)の倍数。.set--s で --s-unit を em 化すると再ベースされる。
// 10 刻みはフィボナッチ数列で縦のリズム用。5 刻みは水平方向の gap / padding 用の中間値で 50 まで。
space: {
'5': 'calc(var(--s-unit) * 0.5)', // ≒ 4px
'10': 'var(--s-unit)', // ≒ 8px
'15': 'calc(var(--s-unit) * 1.5)', // ≒ 12px
'20': 'calc(var(--s-unit) * 2)', // ≒ 16px
'25': 'calc(var(--s-unit) * 2.5)', // ≒ 20px
'30': 'calc(var(--s-unit) * 3)', // ≒ 24px
'35': 'calc(var(--s-unit) * 4)', // ≒ 32px
'40': 'calc(var(--s-unit) * 5)', // ≒ 40px
'45': 'calc(var(--s-unit) * 6.5)', // ≒ 52px
'50': 'calc(var(--s-unit) * 8)', // ≒ 64px
'60': 'calc(var(--s-unit) * 13)', // ≒ 104px
'70': 'calc(var(--s-unit) * 21)', // ≒ 168px
},
// flow: lang スコープ上書きありのため手書き SCSS。
flow: { s: '-' },
// content-size
sz: { xs: '400px', s: '640px', m: '880px', l: '1200px', xl: '1600px' },
} as const; export default {
isContainer: 'is--container',
isWrapper: 'is--wrapper',
isLayer: 'is--layer',
isBoxLink: 'is--boxLink',
isCoverLink: 'is--coverLink',
isSide: 'is--side',
isSkipFlow: 'is--skipFlow',
hasTransition: 'has--transition',
hasGutter: 'has--gutter',
hasSnap: 'has--snap',
hasMask: 'has--mask',
} as const; カスタマイズ例
例えば次のような設定ファイルを用意するとします。
import DEFAULT_CONFIG from 'lism-css/default-config';const { props } = DEFAULT_CONFIG;
export default { props: { // 既存propにpresetsを追加 ta: { presets: [...(props.ta.presets || []), 'justify'] }, // 既存propにutility値を追加 p: { utils: { box: '2em' } }, // 新しいpropの追加(filterはデフォルトに含まれない) filter: { utils: { blur: 'blur(3px)' } }, }, tokens: { // トークンは { key: value } の値マップで定義(既定に deep-merge される) // → :root { --lts--2xl: .5em } の出力と -lts:2xl のユーティリティが自動生成される lts: { '2xl': '.5em' }, }, traits: { // Trait(is--* / has--*)出力用のプロパティを追加 isHoge: 'is--hoge', },};上記のカスタマイズによって、Lismコンポーネントでは次のような挙動が追加されます。
ta='justify'→-ta:justifyを出力する。p='box'→-p:boxを出力する。filter='blur'→-filter:blurを出力する。lts='2xl'→-lts:2xlを出力する。isHoge→is--hogeを出力する。
<Box p="box" ta="justify" filter="blur" lts="2xl" isHoge>Box</Box>
↓ 出力結果
<div class="l--box is--hoge -p:box -ta:justify -filter:blur -lts:2xl">Box</div>統合プラグインを使っていれば、これらのクラスに対応する CSS と型定義も自動で生成されます。統合プラグインを使わない構成では CLI ビルドで CSS を生成してください。
ブレイクポイント(xs / xl)の有効化
デフォルトのブレイクポイントは xs: 0(無効)/ sm: 480px / md: 800px / lg: 1120px / xl: 0(無効)です。値 0 は「無効(CSS クエリを出力しない)」を表します。
xs や xl を有効化するには、lism.config.js の breakpoints に、有効化したいブレイクポイントのサイズだけを指定します。
export default { breakpoints: { xs: '360px', // xs を有効化 xl: '1400px', // xl を有効化 },};これだけで、ブレイクポイント対応(bp: 1)の全 Property Class が xs / xl のブレイクポイント対応クラス(-p_xs / -p_xl など)も出力するようになります。prop ごとに個別指定する必要はありません。
コンポーネント側でもオブジェクト記法で xs / xl を指定できるようになります。
<Box p={{ base: 20, xs: 10, sm: 30 }} />full.css を読み込んでいる場合も、xs / xl はデフォルトでは無効です(出力されるのは sm / md / lg)。利用するには上記と同様に、breakpoints でサイズを指定して有効化してください。
xs / xl などを有効化すると bp: 1 の全 Property Class に該当ブレイクポイントのブロックが追加されるぶん、CSS サイズが増えます。CSS Purge との併用が前提の設計です。CSS Purge を使わない場合は、bp のリスト形式(bp: ['sm', 'md'] など)で prop ごとに出力ブレイクポイントを絞ることを検討してください。
SCSS を直接利用する構成では、$breakpoints の上書きでも有効化できます(SCSS でのカスタマイズを参照)。
isFullMode
全部入りビルド(full.css)を読み込むだけでは、コンポーネント側の出力は変わりません(例: t="20" はインラインスタイルとして出力されます)。コンポーネントの出力も full.css に合わせるには、isFullMode を有効にします。
export default { isFullMode: true,};isFullMode: true にすると、コンポーネントの props 設定に full 用の設定が適用されます。
t="20"のような値が、インラインスタイルではなく-tクラス+--t変数で出力されます。ta={['start', 'center']}のようなブレイクポイント指定が、full.cssにブレイクポイント対応クラスがある Property Class で警告なく使えます。- 例外として、変数出力専用(
isVar系)にはbds/bdcを除いてブレイクポイント対応が追加されません。contentSizeなどのisVar系、単位なし比率を保つため対象外のlh、border ショートハンド系は、isFullModeを有効にしてもブレイクポイント指定で警告が出ます。 - 設定は「デフォルト設定 → full 用設定 →
lism.config.jsのprops」の順でマージされ、後のものほど優先されます。
isFullModeは、full.css(またはisFullModeを有効にして CLI ビルドしたmain.css)を読み込んでいることが前提です。デフォルトのmain.cssのまま有効にすると、出力されたクラスに対応するスタイルが存在しない状態になります。isFullModeはビルド時の設定です。ランタイムのwindow._LISM_CSS_CONFIG_では切り替えられません。
型サポート
型は2種類あります。設定ファイルを書くときの LismConfig と、コンポーネントを使うときの lism-env.d.ts です。
設定ファイルを書くときの型(LismConfig)
lism-css/config-types から LismConfig 型を読み込むと、設定ファイルのキー名や値の形をエディタが補完・チェックしてくれます。props を porps とタイプミスした、といった間違いをその場で見つけられます。
lism.config.ts では satisfies LismConfig を付けるのがおすすめです。型チェックが効きつつ、書いた内容(追加した prop / token のキーなど)の具体的な型はそのまま保たれます。
import type { LismConfig } from 'lism-css/config-types';
export default { props: { filter: { utils: { blur: 'blur(3px)' } }, }, breakpoints: { xs: '360px', },} satisfies LismConfig;lism.config.js(JavaScript)でも、JSDoc の @type を付ければ同じように補完・チェックが効きます。
/** @type {import('lism-css/config-types').LismConfig} */export default { props: { filter: { utils: { blur: 'blur(3px)' } }, },};コンポーネントを使うときの型(lism-env.d.ts)
統合プラグインを使っている場合、lism.config.js の内容は lism-env.d.ts として自動生成され、コンポーネント側の型に反映されます。手書きの型拡張は不要です。lism-env.d.ts は git にコミットしてください。
- 追加した prop / trait は
CustomPropRegistry/CustomTraitRegistryの拡張として出力されます。<Box filter="blur" isHoge>のような新規 prop / trait も、エディタやastro checkで型エラーになりません。 breakpointsで有効化したxs/xlも、オブジェクト記法や配列記法の型・補完に反映されます。手書きで対応する方法は Responsive ページを参照してください。isFullMode: trueのときは、taなどへのブレイクポイント指定(配列・オブジェクト記法)も型エラーになりません。
なお、既存 prop への値追加(ta="justify" など)はもともと任意の文字列を受け付けるため、型エラーにはなりません(補完候補には出ません)。
統合プラグインを使わない構成で isFullMode の型だけ切り替えるには、プロジェクト直下の型定義ファイル(例: src/lism-env.d.ts)で次のように拡張してください。
import 'lism-css';
declare module 'lism-css' { interface FullModeRegistry { enabled: true; // キー名は任意。1つでもキーがあれば full 版の型に切り替わる }}統合プラグインを使わない構成で反映する
統合プラグインを使わない構成では、lism.config.js は自動では反映されません。CSS は @lism-css/plugin 同梱の CLI でビルドします。
コンポーネント側は lism.config.js を読まないため、p="box" のような追加値はクラスとして認識されません。コンポーネントから使うには、Lism Props の :value 記法(p=":box")で強制的にクラス化してください。クライアント側でレンダリングされるコンポーネントなら、ブラウザで window._LISM_CSS_CONFIG_ に同じ内容を定義して実行時にマージすることもできます(isFullMode は除く)。
CLI ビルド
@lism-css/plugin が提供する lism-css build を実行すると、lism.config.js を読み込んで設定を反映した CSS を生成します。
npx lism-css buildカスタマイズ例の設定なら、次のスタイルが lism-css/main.css に生成されます。
.-ta\:justify { text-align: justify;}.-p\:box { padding: 2em;}.-filter\:blur { filter: blur(3px);}.-lts\:2xl { letter-spacing: var(--lts--2xl);}tokens に値を書いておけば、.-lts\:2xl { letter-spacing: var(--lts--2xl) } のようにトークンを参照する Property Class に加えて、参照先のCSS変数(:root { --lts--2xl: .5em })も生成されます。値に '-' を指定したキーは「名前だけを定義し、実値は出力しない」指定です。palette.keycolor のようにCSS変数を持たないトークンや、bdrs.inner のように実値を手書き SCSS 側で定義するトークンで使います。
一方で is-- クラスのスタイルは自動生成されないため、別途手動で定義して読み込ませる必要があります。
@layer lism-trait { .is--hoge { /* ... */ }}- パッケージのスタイルを再ビルドするコマンドのため、パッケージ更新ごとにビルドが必要です。
full.css/full_no_layer.cssはデフォルトでは再ビルドされません。full.cssを使用している場合は--fullオプションを付けてください。isFullModeが有効なら、main.cssにも full 用の設定が適用されます。
npx lism-css build --full