lism.config.js
Place a lism.config.js (or lism.config.ts / lism.config.mjs) in your project root and complete the following setup to extend the props / tokens / traits and breakpoints your components accept.
Using lism.config.js requires @lism-css/plugin. With Vite or Astro, the integrated plugin reflects it automatically in both the components and the CSS. For Next.js, use withLism() instead. For any other setup, only the CSS is built, using the bundled CLI (see Applying config without the integrated plugin).
Setup
Add the lismCss() integrated plugin from @lism-css/plugin to your Vite / Astro config. A single addition enables config loading (lism.config.js), automatic CSS reflection, and automatic generation of the lism-env.d.ts type definitions — all at once.
This plugin is what loads lism.config.js. Without it added to your config, placing lism.config.js in your project has no effect — its contents are not used.
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()],});For Next.js, use withLism() from @lism-css/plugin/next instead of the integrated plugin. See Setting up with Next.js for the setup steps.
The plugin auto-detects the config file from the project root in the order lism.config.ts → lism.config.mjs → lism.config.js. To use a file at a different location, specify the path via the configPath option:
integrations: [lismCss({ configPath: './config/lism.config.js' })],Writing the config file
Format
export default { props: { hoge: { ... }, foo: { ... }, ... }, tokens: { hoge: { ... }, foo: { ... }, ... }, traits: { isHoge: 'is--hoge', setFoo: 'set--foo', ... }, // breakpoints and isFullMode are specified in the same file (see below)};/**
* 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; Customization example
Suppose you have a config file like this:
import DEFAULT_CONFIG from 'lism-css/default-config';const { props } = DEFAULT_CONFIG;
export default { props: { // Add presets to an existing prop ta: { presets: [...(props.ta.presets || []), 'justify'] }, // Add a utility value to an existing prop p: { utils: { box: '2em' } }, // Add a brand-new prop (filter is not in defaults) filter: { utils: { blur: 'blur(3px)' } }, }, tokens: { // Define tokens as a { key: value } map (deep-merged into the defaults) // → emits :root { --lts--2xl: .5em } and auto-generates the -lts:2xl utility lts: { '2xl': '.5em' }, }, traits: { // Add props for Trait (is--* / has--*) output isHoge: 'is--hoge', },};With the above customization, the following behavior is added to Lism components:
ta='justify'→ outputs-ta:justifyp='box'→ outputs-p:boxfilter='blur'→ outputs-filter:blurlts='2xl'→ outputs-lts:2xlisHoge→ outputsis--hoge
<Box p="box" ta="justify" filter="blur" lts="2xl" isHoge>Box</Box>
↓ Output
<div class="l--box is--hoge -p:box -ta:justify -filter:blur -lts:2xl">Box</div>If you’re using the integrated plugin, the CSS and type definitions for these classes are generated automatically too. In setups without the plugin, generate the CSS via a CLI build.
Enabling xs / xl breakpoints
The default breakpoints are xs: 0 (disabled) / sm: 480px / md: 800px / lg: 1120px / xl: 0 (disabled). A value of 0 means “disabled — no CSS query is output”.
To enable xs or xl, specify only the sizes you want to enable in the breakpoints key of lism.config.js:
export default { breakpoints: { xs: '360px', // Enable xs xl: '1400px', // Enable xl },};That’s all it takes. Every Property Class that supports responsive output (bp: 1) will then also emit responsive classes for xs / xl (e.g., -p_xs / -p_xl) — no per-prop configuration is needed.
You can then also use xs / xl in object notation on the component side:
<Box p={{ base: 20, xs: 10, sm: 30 }} />Even when you load full.css, xs / xl are disabled by default (only sm / md / lg are emitted). To use them, enable them by specifying sizes in breakpoints, just as above.
Enabling xs / xl adds a block for each active breakpoint to every Property Class that uses bp: 1, which increases the CSS size. This is designed to be paired with CSS Purge. If you are not using CSS Purge, consider using the list form of bp (e.g., bp: ['sm', 'md']) to restrict which breakpoints are output on a per-prop basis.
For projects using SCSS directly, you can also enable xs / xl via the $breakpoints SCSS variable override (see SCSS Customization).
isFullMode
Importing the full build (full.css) alone does not change the component output (for example, t="20" is still output as an inline style). To align the component output with full.css, enable isFullMode.
export default { isFullMode: true,};With isFullMode: true, the full preset is applied to the component’s props configuration:
- Values like
t="20"are output as the-tclass plus the--tvariable instead of an inline style. - Breakpoint values (such as
ta={['start', 'center']}) can be used without warnings on any Property Class that has breakpoint-responsive classes infull.css. - As an exception, variable-only (
isVar) properties don’t gain breakpoint support, except forbds/bdc.isVarproperties such ascontentSize,lh(excluded because it must keep a unitless ratio), and the border shorthand properties still trigger a warning for breakpoint values, even withisFullModeenabled. - Settings are merged in the order “defaults → full preset →
propsinlism.config.js”, with later entries taking precedence.
isFullModeassumes you are loadingfull.css(or amain.cssrebuilt via the CLI build withisFullModeenabled). If you keep the defaultmain.css, the output classes will have no matching styles.isFullModeis a build-time setting. It cannot be toggled at runtime viawindow._LISM_CSS_CONFIG_.
Type support
There are two kinds of types: LismConfig for writing the config file, and lism-env.d.ts for using components.
Types for writing the config file (LismConfig)
Importing the LismConfig type from lism-css/config-types lets your editor complete and type-check the keys and value shapes of the config file. Mistakes such as writing porps instead of props are caught on the spot.
In lism.config.ts, we recommend adding satisfies LismConfig. It applies type checking while preserving the concrete type of what you wrote (such as the keys of added props / tokens).
import type { LismConfig } from 'lism-css/config-types';
export default { props: { filter: { utils: { blur: 'blur(3px)' } }, }, breakpoints: { xs: '360px', },} satisfies LismConfig;In lism.config.js (JavaScript), adding the JSDoc @type annotation gives you the same completion and type checking.
/** @type {import('lism-css/config-types').LismConfig} */export default { props: { filter: { utils: { blur: 'blur(3px)' } }, },};Types for using components (lism-env.d.ts)
When you use the integrated plugin, the contents of lism.config.js are automatically generated as lism-env.d.ts and reflected in the component types. No hand-written type extension is needed — just commit lism-env.d.ts to git.
- Added props / traits are emitted as
CustomPropRegistry/CustomTraitRegistryaugmentations. New props / traits such as<Box filter="blur" isHoge>won’t raise type errors in your editor or withastro check. xs/xlbreakpoints enabled viabreakpointsare also reflected in the types and completion for object and array notation. For the hand-written equivalent, see the Responsive page.- With
isFullMode: true, breakpoint values (array / object notation) for props liketano longer cause type errors either.
Note that adding values to an existing prop (such as ta="justify") never causes a type error to begin with, since these accept arbitrary strings already (they just won’t appear in completion candidates).
If you’re not using the integrated plugin and only want to switch the isFullMode type, extend the type definition file at your project root (e.g. src/lism-env.d.ts) like this:
import 'lism-css';
declare module 'lism-css' { interface FullModeRegistry { enabled: true; // The key name is arbitrary; any single key switches to the full type variant. }}Applying config without the integrated plugin
Without the integrated plugin, lism.config.js isn’t reflected automatically. Build the CSS using the CLI bundled with @lism-css/plugin.
Since components don’t read lism.config.js in this setup, additional values such as p="box" are not recognized as classes. To use them from a component, force the class output with the Lism Props :value notation (p=":box"). For components rendered client-side, you can also merge the same settings at runtime by defining them in the browser via window._LISM_CSS_CONFIG_ (except for isFullMode).
CLI build
Running lism-css build — provided by @lism-css/plugin — reads lism.config.js and generates CSS with your settings applied.
npx lism-css buildWith the customization example config, this command generates the following styles into 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);}When you give a value in tokens, the build generates both the Property Class that references the token (such as .-lts\:2xl { letter-spacing: var(--lts--2xl) }) and the referenced CSS variable itself (such as :root { --lts--2xl: .5em }). A key whose value is '-' only defines the name and doesn’t output a value — use it for tokens with no CSS variable (such as palette.keycolor) or tokens whose real value is defined in hand-written SCSS (such as bdrs.inner).
is-- class styles, on the other hand, are not generated automatically, so you’ll need to define and load them yourself.
@layer lism-trait { .is--hoge { /* ... */ }}- Since this command rebuilds the package styles, you need to run it again every time the package is updated.
full.css/full_no_layer.cssare not rebuilt by default. If you’re usingfull.css, add the--fulloption. WithisFullModeenabled, the full preset is also applied tomain.css.
npx lism-css build --full