fix(fonts): semantic typography utilities per handoff (7 roles)

Заменяет blanket override из MR !45 на типизированную type scale per
design_handoff_ordinis_mdm/README.md "Scale" section.

Семь semantic @utility в styles.css:
  text-title-xl  22/600   — modal title, section header
  text-title-lg  17/600   — page title в editor
  text-title-md  15/600   — dictionary card title
  text-body      13/400   — workhorse: body, buttons, tabs, inputs
  text-cell      12.5/500 — table cell text
  text-mono      11/500   — Mono: IDs, dates, FK refs (font-mono baked in)
  text-cap       10.5/600 — Tektur UPPERCASE caption (font/uppercase baked in)

Audit phases:
  P1: добавил 7 утилит, font/uppercase/tracking baked где надо
  P2: 43 deterministic codemods (font-mono+text-2xs/[11px] → text-mono,
      [15/17/22]px+font-semibold → text-title-*, [12.5]px → text-cell,
      [10.5]px+uppercase+tracking → text-cap)
  P3: 64 text-sm → text-body (handoff workhorse), 84 text-2xs context-aware
      (TableCell → text-cell, bare → text-cell, плюс cleanup caps в backticks
      template literals который Phase 2 пропустил), 25 text-xs (6 → text-mono
      когда с font-mono, 19 → text-cell), 8 titles text-base/lg → text-title-*
  P4: убрал --text-2xs override (no users), оставил --text-sm: 13px scoped
      к @nstart/ui passthrough (см. comment в styles.css — убирается в Stage 3.9)

Stats:
  text-body: 69 | text-cell: 99 | text-mono: 50 | text-cap: 42
  text-title-xl: 5 | text-title-lg: 5 | text-title-md: 7
  text-sm/text-2xs/text-xs в src/: 0 (только в styles.css комментариях)

Поведение всех 277 typography usages теперь явно соответствует handoff —
каждое место осознанно выбрано под роль, не плажирующий override.
This commit is contained in:
Zimin A.N.
2026-05-11 14:31:35 +03:00
parent e62633d903
commit b94912789f
46 changed files with 296 additions and 243 deletions
+21 -21
View File
@@ -115,7 +115,7 @@ function WebhookDetailPage() {
<div className="space-y-6">
<PageHeader
breadcrumb={
<Link to="/webhooks" className="text-sm text-ink-2 hover:text-accent">
<Link to="/webhooks" className="text-body text-ink-2 hover:text-accent">
{t('nav.webhooks')}
</Link>
}
@@ -156,7 +156,7 @@ function WebhookDetailPage() {
<Panel>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 p-4">
<InfoRow label={t('webhooks.field.url')}>
<code className="font-mono text-2xs break-all">{data.url}</code>
<code className="text-mono break-all">{data.url}</code>
</InfoRow>
<InfoRow label={t('webhooks.col.status')}>
<Badge variant={data.active ? 'success' : 'neutral'}>
@@ -177,28 +177,28 @@ function WebhookDetailPage() {
))}
</div>
) : (
<span className="text-2xs text-mute">{t('webhooks.allScopes')}</span>
<span className="text-cell text-mute">{t('webhooks.allScopes')}</span>
)}
</InfoRow>
<InfoRow label={t('webhooks.field.dictionaryFilter')}>
<span className="text-2xs">
<span className="text-cell">
{data.dictionaryFilter && data.dictionaryFilter.length > 0
? data.dictionaryFilter.join(', ')
: t('webhooks.allDicts')}
</span>
</InfoRow>
<InfoRow label={t('webhooks.field.eventTypeFilter')}>
<span className="text-2xs">
<span className="text-cell">
{data.eventTypeFilter && data.eventTypeFilter.length > 0
? data.eventTypeFilter.join(', ')
: t('webhooks.allEvents')}
</span>
</InfoRow>
<InfoRow label="HMAC secret">
<code className="font-mono text-2xs">{data.hmacSecret}</code>
<code className="text-mono">{data.hmacSecret}</code>
</InfoRow>
<InfoRow label={t('webhooks.field.createdBy')}>
<span className="text-2xs">
<span className="text-cell">
{data.createdBy} · {new Date(data.createdAt).toLocaleString()}
</span>
</InfoRow>
@@ -207,7 +207,7 @@ function WebhookDetailPage() {
<div>
<div className="flex items-center justify-between mb-3">
<h2 className="font-sans text-base text-accent">
<h2 className="font-sans text-title-md text-accent">
{t('webhooks.deliveries.title')}
</h2>
<IconButton
@@ -232,7 +232,7 @@ function WebhookDetailPage() {
onClick={() => handleStatusToggle(s)}
aria-pressed={active}
className={[
'inline-flex items-center gap-1.5 px-2.5 py-1 rounded-sm border text-2xs uppercase tracking-[0.10em] font-display transition-colors',
'text-cap inline-flex items-center gap-1.5 px-2.5 py-1 rounded-sm border transition-colors',
active
? 'border-accent bg-accent/8 text-accent'
: 'border-line bg-white text-ink-2 hover:border-carbon/40',
@@ -249,7 +249,7 @@ function WebhookDetailPage() {
setPage(0)
setStatusFilter(undefined)
}}
className="text-xs text-accent hover:underline ml-2"
className="text-cell text-accent hover:underline ml-2"
>
{t('webhooks.deliveries.filter.clear')}
</button>
@@ -282,7 +282,7 @@ function WebhookDetailPage() {
return (
<TableRow key={d.id}>
<TableCell>
<span className="font-mono text-2xs">{d.outboxEventId}</span>
<span className="text-mono">{d.outboxEventId}</span>
</TableCell>
<TableCell>
<Badge variant={STATUS_BADGE[d.status] ?? 'neutral'}>
@@ -290,22 +290,22 @@ function WebhookDetailPage() {
</Badge>
</TableCell>
<TableCell>
<span className="font-mono text-2xs">{d.attemptCount}</span>
<span className="text-mono">{d.attemptCount}</span>
</TableCell>
<TableCell>
<span className="text-2xs text-ink-2">
<span className="text-cell text-ink-2">
{d.lastAttemptAt
? new Date(d.lastAttemptAt).toLocaleString()
: '—'}
</span>
</TableCell>
<TableCell>
<span className="font-mono text-2xs">
<span className="text-mono">
{d.lastStatusCode ?? '—'}
</span>
</TableCell>
<TableCell>
<span className="text-2xs text-ink-2 line-clamp-2 max-w-md inline-block">
<span className="text-cell text-ink-2 line-clamp-2 max-w-md inline-block">
{d.lastError ?? '—'}
</span>
</TableCell>
@@ -337,7 +337,7 @@ function WebhookDetailPage() {
>
</Button>
<span className="text-sm text-ink-2 self-center">
<span className="text-body text-ink-2 self-center">
{page + 1} / {deliveries.data.totalPages}
</span>
<Button
@@ -384,14 +384,14 @@ function TestResultAlert({
action={
<button
type="button"
className="text-xs text-ink-2 hover:text-ink"
className="text-cell text-ink-2 hover:text-ink"
onClick={onDismiss}
>
</button>
}
>
<div className="space-y-1 text-sm">
<div className="space-y-1 text-body">
{result.httpStatus != null && (
<div>
<span className="text-cap text-mute mr-2">
@@ -409,7 +409,7 @@ function TestResultAlert({
</div>
)}
{result.message && (
<div className="font-mono text-2xs text-ink whitespace-pre-wrap break-all">
<div className="text-mono text-ink whitespace-pre-wrap break-all">
{result.message}
</div>
)}
@@ -446,14 +446,14 @@ function RotateRevealModal({
className="bg-white rounded-lg p-6 max-w-xl w-full space-y-4"
onClick={(e) => e.stopPropagation()}
>
<h3 className="font-sans text-lg text-accent">
<h3 className="font-sans text-title-lg text-accent">
{t('webhooks.secret.rotatedTitle')}
</h3>
<Alert variant="warning" title={t('webhooks.secret.warningTitle')}>
{t('webhooks.secret.rotatedWarning')}
</Alert>
<div className="bg-carbon/5 border border-line rounded-md p-3">
<code className="font-mono text-2xs break-all">{secret}</code>
<code className="text-mono break-all">{secret}</code>
</div>
<div className="flex justify-end">
<Button variant="primary" onClick={onClose}>