style(storybook): refine foundation color chips and typography layout
- Hanmac Color: continuous spectrum band (8/4/2 columns by width), 120px swatches with name, role and HEX inside, Default/Light 4 role lines - Company Colors: separate 120px chips (12px padding, 4px radius), name top left and HEX bottom right; company layout and data unchanged - Typography: Font Family/Weight in two columns with info | preview rows, vertically centered specimens grouped by component, Text Colors chips with 14px Medium names - Shared colorHex/textTone helpers pick the first text tone reaching 4.5:1 - Update brand color guide and WORKING_CONTEXT
This commit is contained in:
1 parent
3ddacb19d5
commit
65cc256cfd
7 files changed
+223
-111
No files matched your search
@@ -1,7 +1,8 @@
|
||||
import { useEffect, useRef, useState } from "react"
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite"
|
||||
import { typographyProperties, typographyReferences } from "./reference-data"
|
||||
import { ReferencePage, ReferenceSection, useResolvedProperties } from "./reference"
|
||||
import { StorySectionGrid } from "../components/story-page"
|
||||
import { colorHex, ReferencePage, ReferenceSection, textTone, textToneProperties, useResolvedProperties } from "./reference"
|
||||
|
||||
type TypographyReference = (typeof typographyReferences)[number]
|
||||
|
||||
@@ -10,7 +11,7 @@ const weights = [
|
||||
{ property: "--hm-font-weight-medium", label: "Medium" },
|
||||
{ property: "--hm-font-weight-bold", label: "Bold" },
|
||||
]
|
||||
// Left table: text styles. Right table: form controls. Together they cover every entry in typographyReferences.
|
||||
// Left column: text styles. Right column: form controls. Together they cover every entry in typographyReferences.
|
||||
const textLabels = ["Page Header · title", "Card · title", "Page Header · description", "Label"]
|
||||
const textRows = typographyReferences.filter(item => textLabels.includes(item.label))
|
||||
const controlRows = typographyReferences.filter(item => !textLabels.includes(item.label))
|
||||
@@ -23,22 +24,24 @@ const textColorRoles = [
|
||||
// Transparent readonly Input on the beige surface uses Secondary (Placeholder is below 4.5:1 there).
|
||||
{ label: "Readonly Placeholder", name: "읽기 전용 입력 안내", token: "--hm-sem-fg-secondary" },
|
||||
]
|
||||
const textColorProperties = [...new Set(textColorRoles.map(item => item.token))]
|
||||
// Chip colors plus the swatch text candidates (textTone, reference.tsx).
|
||||
const textColorProperties = [...new Set([...textColorRoles.map(item => item.token), ...textToneProperties])]
|
||||
|
||||
function toHex(value: string | undefined) {
|
||||
if (!value) return ""
|
||||
if (/^#[\da-f]{6}$/i.test(value)) return value.toUpperCase()
|
||||
const rgb = value.match(/^rgb\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)\s*\)$/)
|
||||
return rgb ? `#${rgb.slice(1).map(channel => Number(channel).toString(16).padStart(2, "0")).join("").toUpperCase()}` : value
|
||||
}
|
||||
|
||||
// Same chip classes as Foundation/한맥 컬러 and 가족사 컬러 (foundation.css): swatch, then role name / usage / HEX on separate lines
|
||||
// so the item name, its description and the HEX value read as three levels (styles scoped to .hm-text-color-row).
|
||||
// Same chip as Foundation/가족사 컬러 (separate chips; shared swatch rules in foundation.css via data-text-colors): role name and
|
||||
// usage at the top left, HEX at the bottom right, text tone by contrast.
|
||||
function TextColorChips({ values }: { values: Record<string, string> }) {
|
||||
return <div className="hm-brand-color-row hm-text-color-row">{textColorRoles.map(item => <article className="hm-brand-color-chip" key={item.label} data-text-color={item.label} data-color-token={item.token}>
|
||||
<div className="hm-foundation-swatch" style={{ background: `var(${item.token})` }} />
|
||||
<h3>{item.label}</h3><p data-color-name>{item.name}</p><p data-color-value>{toHex(values[item.token])}</p>
|
||||
</article>)}</div>
|
||||
return <div className="hm-brand-color-row hm-text-color-row" data-text-colors>{textColorRoles.map(item => {
|
||||
const hex = colorHex(values[item.token])
|
||||
return <article className="hm-brand-color-chip" key={item.label} data-text-color={item.label} data-color-token={item.token} data-text-tone={textTone(hex, values)}>
|
||||
<div className="hm-foundation-swatch" style={{ background: `var(${item.token})` }}>
|
||||
<div className="hm-color-name">
|
||||
<h3>{item.label}</h3>
|
||||
<p className="hm-color-note" data-color-name>{item.name}</p>
|
||||
</div>
|
||||
<p data-color-value>{hex}</p>
|
||||
</div>
|
||||
</article>
|
||||
})}</div>
|
||||
}
|
||||
|
||||
function SpecimenRow({ label, size, line, weight }: TypographyReference & { weight?: string }) {
|
||||
@@ -53,18 +56,23 @@ function SpecimenRow({ label, size, line, weight }: TypographyReference & { weig
|
||||
})
|
||||
return () => { mounted = false }
|
||||
}, [])
|
||||
return <tr>
|
||||
<th scope="row">{label}</th>
|
||||
<td><div ref={ref} className="hm-typo-sample" data-typography-sample={label} lang="ko" style={{ fontSize: `var(${size})`, lineHeight: `var(${line})`, fontWeight: weight ? `var(${weight})` : "normal" }}>한맥 Hanmac 0123</div></td>
|
||||
<td className="hm-typo-metrics" data-typography-metrics><span>{metrics.main}</span><small className="hm-foundation-note">{metrics.spacing}</small></td>
|
||||
</tr>
|
||||
// The sample at its real component size leads; the item name and the computed metrics sit beside it as secondary text.
|
||||
return <li className="hm-typo-row hm-typo-specimen">
|
||||
<div className="hm-typo-row-meta hm-typo-specimen-meta">
|
||||
<span className="hm-typo-specimen-name">{label.split(" · ")[1] ?? label}</span>
|
||||
<span className="hm-typo-metrics" data-typography-metrics><span>{metrics.main}</span><span>{metrics.spacing}</span></span>
|
||||
</div>
|
||||
<div ref={ref} className="hm-typo-row-preview hm-typo-sample" data-typography-sample={label} lang="ko" style={{ fontSize: `var(${size})`, lineHeight: `var(${line})`, fontWeight: weight ? `var(${weight})` : "normal" }}>한맥 Hanmac 0123</div>
|
||||
</li>
|
||||
}
|
||||
|
||||
function SpecimenTable({ rows }: { rows: readonly TypographyReference[] }) {
|
||||
return <table className="hm-typo-table">
|
||||
<thead><tr><th scope="col">Component</th><th scope="col">Sample</th><th scope="col">Size / Line / Weight</th></tr></thead>
|
||||
<tbody>{rows.map(item => <SpecimenRow key={item.label} {...item} />)}</tbody>
|
||||
</table>
|
||||
// Rows grouped by component, the label part before " · " (Page Header, Card, Label, Input, Button Standard).
|
||||
const groupOf = (item: TypographyReference) => item.label.split(" · ")[0]
|
||||
function SpecimenGroups({ rows }: { rows: readonly TypographyReference[] }) {
|
||||
return <div className="hm-typo-groups">{[...new Set(rows.map(groupOf))].map(group => <section className="hm-typo-group" key={group} aria-label={group}>
|
||||
<h3>{group}</h3>
|
||||
<ul>{rows.filter(item => groupOf(item) === group).map(item => <SpecimenRow key={item.label} {...item} />)}</ul>
|
||||
</section>)}</div>
|
||||
}
|
||||
|
||||
function TypographyReferencePage() {
|
||||
@@ -75,21 +83,31 @@ function TypographyReferencePage() {
|
||||
<ReferenceSection title="Text Colors" note="">
|
||||
<TextColorChips values={colorValues} />
|
||||
</ReferenceSection>
|
||||
<ReferenceSection title="Font Family">
|
||||
<div className="hm-typo-family sb-page-indent">
|
||||
<p><strong>Noto Sans KR</strong> <span className="hm-foundation-note">→ 맑은 고딕 → 시스템 sans-serif</span></p>
|
||||
<code className="hm-foundation-note hm-typo-token">--hm-font-family-base: {values["--hm-font-family-base"]}</code>
|
||||
</div>
|
||||
</ReferenceSection>
|
||||
<ReferenceSection title="Font Weight">
|
||||
<div className="hm-typo-weights sb-page-indent">{weights.map(({ property, label }) => <article className="hm-typo-weight" key={property}>
|
||||
<p className="hm-typo-weight-meta"><strong>{label}</strong> <span className="hm-foundation-note">{values[property]}</span></p>
|
||||
<p className="hm-typo-weight-preview" style={{ fontWeight: `var(${property})` }}>한맥 Hanmac 0123</p>
|
||||
<code className="hm-foundation-note hm-typo-token">{property}</code>
|
||||
</article>)}</div>
|
||||
</ReferenceSection>
|
||||
<StorySectionGrid className="hm-typo-type-grid">
|
||||
<ReferenceSection title="Font Family">
|
||||
<div className="hm-typo-rows sb-page-indent"><div className="hm-typo-row hm-typo-family">
|
||||
<div className="hm-typo-row-meta">
|
||||
<p className="hm-typo-kicker">Primary Typeface</p>
|
||||
<p className="hm-typo-family-name">Noto Sans KR</p>
|
||||
</div>
|
||||
<div className="hm-typo-row-preview">
|
||||
<p className="hm-typo-family-preview" lang="ko" style={{ fontFamily: "var(--hm-font-family-base)" }}>한맥 Hanmac 0123</p>
|
||||
<code className="hm-foundation-note hm-typo-token">--hm-font-family-base: {values["--hm-font-family-base"]}</code>
|
||||
</div>
|
||||
</div></div>
|
||||
</ReferenceSection>
|
||||
<ReferenceSection title="Font Weight">
|
||||
<div className="hm-typo-rows hm-typo-weights sb-page-indent">{weights.map(({ property, label }) => <article className="hm-typo-row hm-typo-weight" key={property}>
|
||||
<div className="hm-typo-row-meta">
|
||||
<p className="hm-typo-weight-meta"><strong>{label}</strong> <span className="hm-foundation-note">{values[property]}</span></p>
|
||||
<code className="hm-foundation-note hm-typo-token">{property}</code>
|
||||
</div>
|
||||
<p className="hm-typo-row-preview hm-typo-weight-preview" style={{ fontWeight: `var(${property})` }}>한맥 Hanmac 0123</p>
|
||||
</article>)}</div>
|
||||
</ReferenceSection>
|
||||
</StorySectionGrid>
|
||||
<ReferenceSection title="Typography Specimens">
|
||||
<div className="hm-typo-tables sb-page-indent"><SpecimenTable rows={textRows} /><SpecimenTable rows={controlRows} /></div>
|
||||
<div className="hm-typo-specimens sb-page-indent"><SpecimenGroups rows={textRows} /><SpecimenGroups rows={controlRows} /></div>
|
||||
</ReferenceSection>
|
||||
</ReferencePage>
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user