refactor(design): lock the border-radius ladder, one radius per role (#1607)
Seven radii were in circulation (4/5/6/8/12/16px + pill) with no rule for which went where; one toolbar row on /transactions mixed four shape languages. This locks a 4-tier ladder (design.md convention 16): - pill: interactive toolbar controls (buttons, chips, pickers, segmented controls, toolbar search, count nubs) - rounded-xl (12px): overlay tier: page panel, dialogs, slide-overs - rounded-lg (8px): cards, form fields, popover/menu content, boxes - rounded-sm (4px): nested leaves (menu items, checkboxes, kbd/code nubs) Changes: - New SegmentedControl primitive (pill-in-pill tablist, h-8) replaces the hand-rolled bg-muted/70 tablist copied across 11 files - New ToolbarSearch primitive (pill, h-8) adopted on 9 page toolbars; dialog/picker searches keep the rounded-lg Input - dialog.tsx 8px -> 12px, matching SettingsModal/slide-over/CommandPalette - ContextPicker chips at the shared h-8 toolbar height - ~300 rounded-md / bare rounded call sites remapped by role; auth icon tiles and the mobile nav sheet come down from 16px to 12px - rounded-md, bare rounded, rounded-2xl and rounded-[Npx] are dead vocabulary, enforced by a new off-ladder-radius check in check:guards Verified: lint 0 errors, 14422 unit tests pass, check:guards green, tsc clean on all changed files, sandbox screenshots of transactions/ bookkeeping/granskning toolbars and the Ny verifikation dialog. Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Jakob Wennberg
Claude Fable 5
parent
18c20e68e6
commit
9686b54b41
@@ -75,7 +75,7 @@ export default function BankSyncSinceLastVisit() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="inline-flex items-center gap-2 rounded-md border border-border bg-secondary px-2.5 py-1 text-xs text-foreground">
|
||||
<div className="inline-flex items-center gap-2 rounded-full border border-border bg-secondary px-2.5 py-1 text-xs text-foreground">
|
||||
<span>
|
||||
{count === 1
|
||||
? t('bank_sync_new_since_last_visit_one')
|
||||
|
||||
@@ -110,7 +110,7 @@ export default function BankSyncStatusChip() {
|
||||
return (
|
||||
<Link
|
||||
href="/settings/banking"
|
||||
className="inline-flex items-center gap-1.5 rounded-md border border-destructive/40 bg-destructive/5 px-2.5 py-1 text-xs text-destructive transition-colors hover:bg-destructive/10"
|
||||
className="inline-flex items-center gap-1.5 rounded-full border border-destructive/40 bg-destructive/5 px-2.5 py-1 text-xs text-destructive transition-colors hover:bg-destructive/10"
|
||||
>
|
||||
<AlertTriangle className="h-3.5 w-3.5" />
|
||||
<span>
|
||||
@@ -128,7 +128,7 @@ export default function BankSyncStatusChip() {
|
||||
return (
|
||||
<Link
|
||||
href="/settings/banking"
|
||||
className="inline-flex items-center gap-1.5 rounded-md border border-border bg-muted/30 px-2.5 py-1 text-xs text-attn transition-colors hover:bg-muted/50"
|
||||
className="inline-flex items-center gap-1.5 rounded-full border border-border bg-muted/30 px-2.5 py-1 text-xs text-attn transition-colors hover:bg-muted/50"
|
||||
>
|
||||
<AlertTriangle className="h-3.5 w-3.5" />
|
||||
<span>
|
||||
@@ -143,7 +143,7 @@ export default function BankSyncStatusChip() {
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span className="inline-flex cursor-help items-center gap-1.5 rounded-md border border-border bg-muted/30 px-2.5 py-1 text-xs text-muted-foreground">
|
||||
<span className="inline-flex cursor-help items-center gap-1.5 rounded-full border border-border bg-muted/30 px-2.5 py-1 text-xs text-muted-foreground">
|
||||
<RefreshCw className="h-3.5 w-3.5" />
|
||||
<span>
|
||||
{t('bank_sync_auto_nightly')}
|
||||
|
||||
@@ -212,7 +212,7 @@ export default function DuplicateBookingDialog({
|
||||
{canIgnore && <> {t('dialog_duplicate_ignore_hint')}</>}
|
||||
</p>
|
||||
{candidate && (
|
||||
<div className="space-y-1 rounded-md border bg-muted/30 p-3">
|
||||
<div className="space-y-1 rounded-lg border bg-muted/30 p-3">
|
||||
<div className="flex items-center justify-between gap-3 text-sm">
|
||||
<span className="font-medium">
|
||||
{candidate.voucher_label
|
||||
|
||||
@@ -879,7 +879,7 @@ export default function InvoiceMatchDialog({
|
||||
{/* Side toggle: segmented control. Clicking either
|
||||
button picks that side; the amount stays the
|
||||
same. */}
|
||||
<div className="inline-flex rounded-md border bg-background overflow-hidden h-9">
|
||||
<div className="inline-flex rounded-lg border bg-background overflow-hidden h-9">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => updateEditLine(i, { side: 'debit' })}
|
||||
|
||||
@@ -159,7 +159,7 @@ export default function TransactionAttachDocumentDialog({
|
||||
disabled={isAttaching}
|
||||
/>
|
||||
{pickedDoc && (
|
||||
<div className="flex items-center gap-2 text-sm py-1.5 px-2 rounded bg-muted/50">
|
||||
<div className="flex items-center gap-2 text-sm py-1.5 px-2 rounded-sm bg-muted/50">
|
||||
<FileText className="h-4 w-4 text-muted-foreground shrink-0" />
|
||||
<span className="truncate flex-1">
|
||||
{pickedDoc.supplier_name ?? pickedDoc.file_name}
|
||||
|
||||
@@ -311,7 +311,7 @@ export default function TransactionBookingDialog({
|
||||
{pickedInboxDocs.map((doc) => (
|
||||
<div
|
||||
key={doc.document_id}
|
||||
className="flex items-center gap-2 text-sm py-1.5 px-2 rounded bg-muted/50"
|
||||
className="flex items-center gap-2 text-sm py-1.5 px-2 rounded-sm bg-muted/50"
|
||||
>
|
||||
<FileText className="h-4 w-4 text-muted-foreground shrink-0" />
|
||||
<span className="truncate flex-1">
|
||||
|
||||
@@ -6,6 +6,7 @@ import Link from 'next/link'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { DataListEmpty } from '@/components/ui/data-list'
|
||||
import { SegmentedControl } from '@/components/ui/segmented-control'
|
||||
import { TH_CLASS, TD_CLASS, QUIET_LINK_CLASS } from '@/components/ui/dry-table'
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -148,25 +149,11 @@ export default function TransactionHistoryList({
|
||||
<div className="space-y-4">
|
||||
{/* Business/private seg; the source chip lives in the page toolbar. */}
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<div className="inline-flex shrink-0 gap-0.5 rounded-lg bg-muted/70 p-[3px]" role="tablist">
|
||||
{FILTERS.map(({ key, labelKey }) => (
|
||||
<button
|
||||
key={key}
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-selected={filter === key}
|
||||
onClick={() => setFilter(key)}
|
||||
className={cn(
|
||||
'rounded-md px-3.5 py-[5px] text-[12.5px] transition-colors duration-150',
|
||||
filter === key
|
||||
? 'border border-border bg-card font-medium text-foreground'
|
||||
: 'text-muted-foreground hover:text-foreground',
|
||||
)}
|
||||
>
|
||||
{t(labelKey)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<SegmentedControl
|
||||
value={filter}
|
||||
onChange={setFilter}
|
||||
options={FILTERS.map(({ key, labelKey }) => ({ value: key, label: t(labelKey) }))}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{filtered.length === 0 ? (
|
||||
|
||||
@@ -46,7 +46,7 @@ export default function VatTreatmentSelect<T extends VatSelectValue>({
|
||||
key={opt.value}
|
||||
value={opt.value}
|
||||
className={cn(
|
||||
'relative flex w-full cursor-default select-none items-start rounded-md py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-secondary focus:text-secondary-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50'
|
||||
'relative flex w-full cursor-default select-none items-start rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-secondary focus:text-secondary-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50'
|
||||
)}
|
||||
>
|
||||
<span className="absolute left-2 top-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
|
||||
Reference in New Issue
Block a user