improvement(emcn): let every primitive inherit the document font weight (#6291)

* improvement(emcn): let every primitive inherit the document font weight

#6241 flattened the type scale by deleting the tailwind `fontWeight` override
that remapped `font-medium` to 440/480. Nothing was restyled, but the utility's
meaning changed: every one of ~505 call sites written when `font-medium` sat
~10 units above body snapped to a stock 500 against a body that also dropped
430 -> 400. The tab strip and table header Emir reported are two symptoms.

The same PR added the "Font Weight" section to sim-styling.md declaring the end
state (400 default, weight class only to step up) without migrating the codebase
to it, so the rule and its violations shipped together and no gate could flag it.

Removes the hardcoded weight from the ~20 pre-chip emcn primitives so they
inherit 400, matching the chip family that was already correct. Deletes the
three `font-normal` overrides that existed only to undo those defaults
(`TabStrip`, `ChipCombobox`, `ChipModalField`) — their TSDoc said as much.

`<th>` needed care in the other direction: Preflight resets h1-h6 but not `th`,
so a header keeps the UA bold 700 and `font-medium` was holding it *down*.
Deleting it made headers heavier. Rather than neutralize per call site — the
codebase had already accumulated 12 such workarounds — globals.css completes
Preflight with `th { font-weight: inherit }`, and the 8 now-dead `font-normal`
workarounds come out. The rule keeps no element-level exceptions.

Two width-measurement mirrors had to move with the text they measure:
table-grid's auto-fit canvas and tag-input's hidden sizing span. Left stale,
both would mis-measure.

Also: drops an inline `font-weight:500` from the header drag ghost, normalizes
`text-[13px]`/`text-[11px]` to `text-small`/`text-xs`, aligns the landing table
previews that clone the product header, documents AvatarFallback's deliberate
step-up, and corrects the stale AGENTS.md line claiming Button owns a weight.

Verified: typecheck 0, lint clean, full vitest run identical to origin/staging
across three runs (158 failed suites / 1 failed test / 16350 passing — all
pre-existing: a PostCSS env error and a missing `rg` binary).

* fix(tables): align the workflow-group drag ghost with the label it previews

Cursor Bugbot caught the sibling of the ghost fixed in column-header-menu:
this one kept an inline `font-weight:500` after the band label dropped to
the inherited 400, so the drag preview no longer matched the text it
represents — the same mirror drift this PR fixes for the width-measurement
spans.

It was drifted on size too: hardcoded `font-size:13px` against a label that
is `text-xs` (11px), so it never matched. Both now come from the token the
label uses, which also clears the inline fontWeight the styling rule bans.

* fix(toolbar): stop the block drag preview hardcoding a weight its label does not use

Third instance of the same mirror drift, found by sweeping rather than
waiting for it to be reported. The toolbar item label renders at the
inherited 400 (its container is `chipVariants`, which is weight-free), but
the drag preview hardcoded `font-weight: 500` — so the preview never matched
the item it previews, before or after this PR.

Drops the weight only. The 16px is left alone: a drag preview reading larger
than its source is a deliberate affordance, not a mismatch.

Not fixed here, deliberately: the `font-weight: 500` in code-editor.tsx's
highlight layer. That HTML is rendered in registration with a transparent
textarea, so weight affects glyph advance widths and the caret alignment —
it needs live in-editor verification, unlike a detached drag ghost. Its two
placeholder branches already disagree on weight, so it wants its own change.

* revert(canvas): drop the workflow-editor panel changes from this PR

The canvas panel is under active modification elsewhere, so this PR stays
out of it. Reverts the toolbar search input, the sub-block table cell and
its overlay mirror, the messages-input textarea and its mirror, and the
block drag preview — all back to staging verbatim.

Cursor Bugbot was right about that last one and I was wrong: the preview
mirrors the drag DESTINATION (its TSDoc says "looks like a workflow block",
and 250px/16px are block-card dimensions), not the toolbar chip I had
compared it against. workflow-block-view renders the title `font-medium
text-md`, so its 500 was correct. Moot now that the file is reverted, but
worth recording so the next sweep does not repeat the mistake.

The globals `th` rule still covers the sub-block table header without a
call-site class; the explicit font-medium there simply wins over it, exactly
as staging renders today.
This commit is contained in:
Waleed
2026-08-05 11:21:46 -07:00
committed by GitHub
parent 402f862210
commit 0583e2085f
33 changed files with 101 additions and 113 deletions
@@ -236,10 +236,7 @@ export function LandingPreviewLogs() {
<thead className='border-[var(--border)] border-b'>
<tr>
{COL_HEADERS.map(({ key, label }) => (
<th
key={key}
className='h-10 px-6 py-1.5 text-left align-middle font-normal text-caption'
>
<th key={key} className='h-10 px-6 py-1.5 text-left align-middle text-caption'>
<button
type='button'
onClick={() => handleSort(key)}
@@ -131,10 +131,7 @@ export function LandingPreviewResource({
<thead className='border-[var(--border)] border-b'>
<tr>
{columns.map((col) => (
<th
key={col.id}
className='h-10 px-6 py-1.5 text-left align-middle font-normal text-caption'
>
<th key={col.id} className='h-10 px-6 py-1.5 text-left align-middle text-caption'>
<button
type='button'
onClick={() => handleSortClick(col.id)}
@@ -467,7 +467,7 @@ function SpreadsheetView({ tableId, tableName, onBack }: SpreadsheetViewProps) {
<th key={col.id} className={CELL_HEADER}>
<div className='flex h-full w-full min-w-0 items-center px-2 py-[7px]'>
<Icon className='size-3 shrink-0 text-[var(--text-icon)]' />
<span className='ml-1.5 min-w-0 overflow-clip text-ellipsis whitespace-nowrap font-medium text-[var(--text-primary)] text-small'>
<span className='ml-1.5 min-w-0 overflow-clip text-ellipsis whitespace-nowrap text-[var(--text-primary)] text-small'>
{col.label}
</span>
<ChevronDown className='ml-auto size-[14px] shrink-0 text-[var(--text-muted)]' />
@@ -214,7 +214,7 @@ export function KnowledgeHeroLoop() {
{COL_HEADERS.map((header) => (
<th
key={header}
className='h-10 px-6 py-1.5 text-left align-middle font-normal text-[var(--text-muted)] text-caption'
className='h-10 px-6 py-1.5 text-left align-middle text-[var(--text-muted)] text-caption'
>
{header}
</th>
@@ -335,7 +335,7 @@ export function LogsHeroLoop() {
{COL_HEADERS.map((label) => (
<th
key={label}
className='h-10 px-6 py-1.5 text-left align-middle font-normal text-[var(--text-muted)] text-caption'
className='h-10 px-6 py-1.5 text-left align-middle text-[var(--text-muted)] text-caption'
>
{label}
</th>
@@ -316,7 +316,7 @@ function TablesGridPane({ rowCount, filledCount }: TablesGridPaneProps) {
<th key={column.id} className={CELL_HEADER}>
<div className='flex h-full w-full min-w-0 items-center px-2 py-[7px]'>
<Icon className='size-3 shrink-0 text-[var(--text-icon)]' />
<span className='ml-1.5 min-w-0 overflow-clip text-ellipsis whitespace-nowrap font-medium text-[var(--text-primary)] text-small'>
<span className='ml-1.5 min-w-0 overflow-clip text-ellipsis whitespace-nowrap text-[var(--text-primary)] text-small'>
{column.label}
</span>
<ChevronDown className='ml-auto size-[14px] shrink-0 text-[var(--text-muted)]' />
+9
View File
@@ -658,6 +658,15 @@ html.sidebar-booting .sidebar-shell-inner {
letter-spacing: 0.28px;
}
/* Completes Preflight, which resets h1-h6 to `inherit` but leaves `th` at the
UA `bold`. Without this a table header, and every label, input, and button
inside it, silently renders at 700 — so `font-medium` on a header reads as
a step DOWN, and removing it makes the header heavier. Normalized here once
rather than neutralized at each `<th>`. */
th {
font-weight: inherit;
}
/* Ensure visible text caret across inputs and editors */
input,
textarea,
@@ -71,7 +71,7 @@ export function NewColumnDropdown({
onClick={blocked ? onBlocked : undefined}
>
<Plus className='size-[14px] shrink-0 text-[var(--text-icon)]' />
<span className='font-medium text-[var(--text-body)] text-small'>New column</span>
<span className='text-[var(--text-body)] text-small'>New column</span>
</button>
)
@@ -162,8 +162,9 @@ export const ColumnHeaderMenu = React.memo(function ColumnHeaderMenu({
const ghost = document.createElement('div')
ghost.textContent = ghostLabel
ghost.className = 'text-small'
ghost.style.cssText =
'position:absolute;top:-9999px;padding:4px 8px;background:var(--bg);border:1px solid var(--border);border-radius:4px;font-size:13px;font-weight:500;white-space:nowrap;color:var(--text-primary)'
'position:absolute;top:-9999px;padding:4px 8px;background:var(--bg);border:1px solid var(--border);border-radius:4px;white-space:nowrap;color:var(--text-primary)'
document.body.appendChild(ghost)
e.dataTransfer.setDragImage(ghost, ghost.offsetWidth / 2, ghost.offsetHeight / 2)
requestAnimationFrame(() => ghost.parentNode?.removeChild(ghost))
@@ -284,7 +285,7 @@ export const ColumnHeaderMenu = React.memo(function ColumnHeaderMenu({
if (e.key === 'Escape') onRenameCancel()
}}
onBlur={onRenameSubmit}
className='ml-1.5 min-w-0 flex-1 border-0 bg-transparent p-0 font-medium text-[var(--text-primary)] text-small outline-none focus:outline-none focus:ring-0'
className='ml-1.5 min-w-0 flex-1 border-0 bg-transparent p-0 text-[var(--text-primary)] text-small outline-none focus:outline-none focus:ring-0'
/>
</div>
) : readOnly ? (
@@ -295,7 +296,7 @@ export const ColumnHeaderMenu = React.memo(function ColumnHeaderMenu({
blockIconInfo={sourceInfo?.blockIconInfo}
blockMissing={blockMissing}
/>
<span className='ml-1.5 min-w-0 overflow-clip text-ellipsis whitespace-nowrap font-medium text-[13px] text-[var(--text-primary)]'>
<span className='ml-1.5 min-w-0 overflow-clip text-ellipsis whitespace-nowrap text-[var(--text-primary)] text-small'>
{column.workflowGroupId ? column.headerLabel : column.name}
</span>
</div>
@@ -313,7 +314,7 @@ export const ColumnHeaderMenu = React.memo(function ColumnHeaderMenu({
blockIconInfo={sourceInfo?.blockIconInfo}
blockMissing={blockMissing}
/>
<span className='ml-1.5 min-w-0 overflow-clip text-ellipsis whitespace-nowrap font-medium text-[var(--text-primary)] text-small'>
<span className='ml-1.5 min-w-0 overflow-clip text-ellipsis whitespace-nowrap text-[var(--text-primary)] text-small'>
{column.workflowGroupId ? column.headerLabel : column.name}
</span>
</button>
@@ -368,8 +368,9 @@ export function WorkflowGroupMetaCell({
const ghost = document.createElement('div')
ghost.textContent = name
ghost.className = 'text-xs'
ghost.style.cssText =
'position:absolute;top:-9999px;padding:4px 8px;background:var(--bg);border:1px solid var(--border);border-radius:4px;font-size:13px;font-weight:500;white-space:nowrap;color:var(--text-primary)'
'position:absolute;top:-9999px;padding:4px 8px;background:var(--bg);border:1px solid var(--border);border-radius:4px;white-space:nowrap;color:var(--text-primary)'
document.body.appendChild(ghost)
e.dataTransfer.setDragImage(ghost, ghost.offsetWidth / 2, ghost.offsetHeight / 2)
requestAnimationFrame(() => ghost.parentNode?.removeChild(ghost))
@@ -438,9 +439,7 @@ export function WorkflowGroupMetaCell({
) : (
<Workflow className='size-[12px] shrink-0 text-[var(--text-icon)]' />
)}
<span className='min-w-0 truncate font-medium text-[11px] text-[var(--text-secondary)]'>
{name}
</span>
<span className='min-w-0 truncate text-[var(--text-secondary)] text-xs'>{name}</span>
{onRunColumn && (
<DropdownMenu open={runMenuOpen} onOpenChange={setRunMenuOpen}>
<DropdownMenuTrigger asChild>
@@ -1687,11 +1687,10 @@ export function TableGrid({
host.appendChild(measure)
try {
measure.className = 'font-medium text-small'
measure.className = 'text-small'
measure.textContent = column.headerLabel
maxWidth = Math.max(maxWidth, measure.getBoundingClientRect().width + 57)
measure.className = 'text-small'
for (const row of currentRows) {
const val = row.data[column.key]
if (val == null) continue
+1 -1
View File
@@ -6,6 +6,6 @@ These rules apply to `packages/emcn/**`.
- Use Radix UI primitives for accessibility where applicable.
- Use CVA when a component has 2+ variants; use direct `className` composition for single-style components.
- Export both the component and its variants helper when using CVA.
- Keep tokens consistent with the chip-pill canonical look: normal font-weight, `--text-body` value text, `--text-icon` icons at `size-[14px]`, `rounded-lg`. Components own their exact tokens (e.g. `Button` uses `rounded-[5px]`+`font-medium`). See `.claude/rules/emcn-components.md` for the full chip-chrome reference.
- Keep tokens consistent with the chip-pill canonical look: normal font-weight, `--text-body` value text, `--text-icon` icons at `size-[14px]`, `rounded-lg`. Components own their exact geometry tokens (e.g. `Button` uses `rounded-[5px]`), but never their own font-weight — every primitive inherits the document 400, and a weight class is reached for only to step deliberately up. See `.claude/rules/emcn-components.md` for the full chip-chrome reference.
- Prefer `transition-colors` for interactive hover and active states.
- Use TSDoc when documenting public components or APIs.
@@ -133,6 +133,11 @@ AvatarImage.displayName = 'AvatarImage'
/**
* Fallback component for Avatar. Displays initials or icon when image is unavailable.
*
* Carries the package's only hardcoded `font-medium`, and deliberately: one or
* two capitals at `text-xs` on a filled disc are a glyph, not running text, and
* need the extra mass to read at avatar sizes. This is the sanctioned "step up
* from body" — every other primitive inherits the document 400.
*/
const AvatarFallback = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Fallback>,
+30 -33
View File
@@ -5,41 +5,38 @@ import { cn } from '../../lib/cn'
/** Shared base styles for status color badge variants */
const STATUS_BASE = 'gap-1.5 rounded-md'
const badgeVariants = cva(
'inline-flex items-center font-medium focus:outline-none transition-colors',
{
variants: {
variant: {
default:
'gap-1 rounded-[40px] border border-[var(--border)] text-[var(--text-secondary)] bg-[var(--surface-4)] hover-hover:text-[var(--text-primary)] hover-hover:border-[var(--border-1)] hover-hover:bg-[var(--surface-6)] dark:hover-hover:bg-[var(--surface-5)]',
outline:
'gap-1 rounded-[40px] border border-[var(--border-1)] bg-transparent text-[var(--text-secondary)] hover-hover:text-[var(--text-primary)] hover-hover:bg-[var(--surface-5)] dark:hover-hover:bg-transparent dark:hover-hover:border-[var(--surface-6)]',
type: 'gap-1 rounded-[40px] border border-[var(--border)] text-[var(--text-secondary)] bg-[var(--surface-4)] dark:bg-[var(--surface-6)]',
green: `${STATUS_BASE} bg-[var(--badge-success-bg)] text-[var(--badge-success-text)]`,
red: `${STATUS_BASE} bg-[var(--badge-error-bg)] text-[var(--badge-error-text)]`,
gray: `${STATUS_BASE} bg-[var(--badge-gray-bg)] text-[var(--badge-gray-text)]`,
blue: `${STATUS_BASE} bg-[var(--badge-blue-bg)] text-[var(--badge-blue-text)]`,
'blue-secondary': `${STATUS_BASE} bg-[var(--badge-blue-secondary-bg)] text-[var(--badge-blue-secondary-text)]`,
purple: `${STATUS_BASE} bg-[var(--badge-purple-bg)] text-[var(--badge-purple-text)]`,
orange: `${STATUS_BASE} bg-[var(--badge-orange-bg)] text-[var(--badge-orange-text)]`,
amber: `${STATUS_BASE} bg-[var(--badge-amber-bg)] text-[var(--badge-amber-text)]`,
teal: `${STATUS_BASE} bg-[var(--badge-teal-bg)] text-[var(--badge-teal-text)]`,
cyan: `${STATUS_BASE} bg-[var(--badge-cyan-bg)] text-[var(--badge-cyan-text)]`,
pink: `${STATUS_BASE} bg-[var(--badge-pink-bg)] text-[var(--badge-pink-text)]`,
'gray-secondary': `${STATUS_BASE} bg-[var(--surface-4)] text-[var(--text-secondary)]`,
},
size: {
sm: 'px-[7px] py-[1px] text-xs',
md: 'px-[9px] py-0.5 text-caption',
lg: 'px-[9px] py-[2.25px] text-caption',
},
const badgeVariants = cva('inline-flex items-center focus:outline-none transition-colors', {
variants: {
variant: {
default:
'gap-1 rounded-[40px] border border-[var(--border)] text-[var(--text-secondary)] bg-[var(--surface-4)] hover-hover:text-[var(--text-primary)] hover-hover:border-[var(--border-1)] hover-hover:bg-[var(--surface-6)] dark:hover-hover:bg-[var(--surface-5)]',
outline:
'gap-1 rounded-[40px] border border-[var(--border-1)] bg-transparent text-[var(--text-secondary)] hover-hover:text-[var(--text-primary)] hover-hover:bg-[var(--surface-5)] dark:hover-hover:bg-transparent dark:hover-hover:border-[var(--surface-6)]',
type: 'gap-1 rounded-[40px] border border-[var(--border)] text-[var(--text-secondary)] bg-[var(--surface-4)] dark:bg-[var(--surface-6)]',
green: `${STATUS_BASE} bg-[var(--badge-success-bg)] text-[var(--badge-success-text)]`,
red: `${STATUS_BASE} bg-[var(--badge-error-bg)] text-[var(--badge-error-text)]`,
gray: `${STATUS_BASE} bg-[var(--badge-gray-bg)] text-[var(--badge-gray-text)]`,
blue: `${STATUS_BASE} bg-[var(--badge-blue-bg)] text-[var(--badge-blue-text)]`,
'blue-secondary': `${STATUS_BASE} bg-[var(--badge-blue-secondary-bg)] text-[var(--badge-blue-secondary-text)]`,
purple: `${STATUS_BASE} bg-[var(--badge-purple-bg)] text-[var(--badge-purple-text)]`,
orange: `${STATUS_BASE} bg-[var(--badge-orange-bg)] text-[var(--badge-orange-text)]`,
amber: `${STATUS_BASE} bg-[var(--badge-amber-bg)] text-[var(--badge-amber-text)]`,
teal: `${STATUS_BASE} bg-[var(--badge-teal-bg)] text-[var(--badge-teal-text)]`,
cyan: `${STATUS_BASE} bg-[var(--badge-cyan-bg)] text-[var(--badge-cyan-text)]`,
pink: `${STATUS_BASE} bg-[var(--badge-pink-bg)] text-[var(--badge-pink-text)]`,
'gray-secondary': `${STATUS_BASE} bg-[var(--surface-4)] text-[var(--text-secondary)]`,
},
defaultVariants: {
variant: 'default',
size: 'md',
size: {
sm: 'px-[7px] py-[1px] text-xs',
md: 'px-[9px] py-0.5 text-caption',
lg: 'px-[9px] py-[2.25px] text-caption',
},
}
)
},
defaultVariants: {
variant: 'default',
size: 'md',
},
})
/** Color variants that support dot indicators */
const STATUS_VARIANTS = [
@@ -100,7 +100,7 @@ function ButtonGroup({
}
const buttonGroupItemVariants = cva(
'inline-flex items-center justify-center font-medium transition-colors outline-none focus:outline-none focus-visible:outline-none disabled:pointer-events-none disabled:opacity-70 px-2 py-1 text-caption border',
'inline-flex items-center justify-center transition-colors outline-none focus:outline-none focus-visible:outline-none disabled:pointer-events-none disabled:opacity-70 px-2 py-1 text-caption border',
{
variants: {
active: {
@@ -21,7 +21,7 @@ import { cn } from '../../lib/cn'
* @example <Button variant='quiet' size='icon' aria-label='Dismiss'><X className='size-[16px]' /></Button>
*/
const buttonVariants = cva(
'inline-flex items-center justify-center font-medium transition-colors disabled:pointer-events-none disabled:opacity-70 outline-none focus:outline-none focus-visible:outline-none rounded-[5px]',
'inline-flex items-center justify-center transition-colors disabled:pointer-events-none disabled:opacity-70 outline-none focus:outline-none focus-visible:outline-none rounded-[5px]',
{
variants: {
variant: {
@@ -11,8 +11,9 @@ import { Combobox, type ComboboxProps } from '../combobox/combobox'
* Reuses 100% of `Combobox` — search, editable entry, multi-select, groups,
* async loading, per-option icons, and `overlayContent` all work unchanged.
* Only the trigger chrome is overridden (the `className` merges last in
* `Combobox`, so `rounded-lg` / height / dark surface and the chip typography
* — normal weight, `--text-body` — win over the heavier combobox defaults).
* `Combobox`, so `rounded-lg` / height / dark surface and the chip `--text-body`
* color win over the combobox defaults). Weight is no longer overridden here —
* `Combobox` inherits the document's 400, which is already the chip weight.
* The muted placeholder still applies because the combobox tints the inner
* label span with `--text-muted` independently of the trigger className.
*
@@ -27,7 +28,7 @@ export function ChipCombobox({ className, ...props }: ComboboxProps) {
<Combobox
{...props}
className={cn(
'h-[30px] rounded-lg font-normal text-[var(--text-body)] dark:bg-[var(--surface-4)]',
'h-[30px] rounded-lg text-[var(--text-body)] dark:bg-[var(--surface-4)]',
className
)}
/>
@@ -623,10 +623,7 @@ function ChipModalField(props: ChipModalFieldProps) {
return (
<div className={cn('flex flex-col gap-[9px]', flush ? 'px-0' : 'px-2', className)}>
<Label
htmlFor={associatesLabel ? id : undefined}
className='pl-0.5 font-normal text-[var(--text-muted)]'
>
<Label htmlFor={associatesLabel ? id : undefined} className='pl-0.5 text-[var(--text-muted)]'>
{title}
{required && (
<span aria-hidden className='ml-0.5 text-[var(--text-error)]'>
+3 -3
View File
@@ -481,7 +481,7 @@ function Container({ children, className, style, onDragOver, onDrop }: CodeConta
className={cn(
// Base container styling
'group relative min-h-[100px] rounded-sm border border-[var(--border-1)]',
'bg-[var(--surface-1)] font-medium font-mono text-sm transition-colors',
'bg-[var(--surface-1)] font-mono text-sm transition-colors',
'dark:bg-[var(--code-bg)]',
// Overflow handling for long content
'overflow-x-auto overflow-y-auto',
@@ -544,7 +544,7 @@ export function getCodeEditorProps(options?: {
padding: 8,
className: cn(
// Base editor classes
'bg-transparent font-[inherit] text-[inherit] font-medium',
'bg-transparent font-[inherit] text-[inherit]',
'text-[var(--text-primary)] dark:text-[var(--code-foreground)]',
'leading-[21px] outline-none focus:outline-none',
'min-h-[106px]',
@@ -1079,7 +1079,7 @@ const VirtualizedViewerInner = memo(function VirtualizedViewerInner({
ref={setRefs}
className={cn(
'code-editor-theme relative rounded-sm border border-[var(--border-1)]',
'bg-[var(--surface-1)] font-medium font-mono text-sm',
'bg-[var(--surface-1)] font-mono text-sm',
wrapText ? 'overflow-x-hidden' : 'overflow-x-auto',
'overflow-y-auto',
'dark:bg-[var(--code-bg)]',
@@ -45,9 +45,7 @@ export function CollapsibleCard({
onKeyDown={(event) => handleKeyboardActivation(event, onToggleCollapse)}
>
<div className='flex min-w-0 flex-1 items-center gap-2'>
<span className='block truncate font-medium text-[var(--text-tertiary)] text-sm'>
{title}
</span>
<span className='block truncate text-[var(--text-tertiary)] text-sm'>{title}</span>
{badge}
</div>
</div>
@@ -21,7 +21,7 @@ import { Input } from '../input/input'
import { Popover, PopoverAnchor, PopoverContent, PopoverScrollArea } from '../popover/popover'
const comboboxVariants = cva(
'flex w-full rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)] px-2 font-sans font-medium text-[var(--text-primary)] placeholder:text-[var(--text-muted)] outline-none disabled:cursor-not-allowed disabled:opacity-50',
'flex w-full rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)] px-2 font-sans text-[var(--text-primary)] placeholder:text-[var(--text-muted)] outline-none disabled:cursor-not-allowed disabled:opacity-50',
{
variants: {
variant: {
@@ -572,7 +572,7 @@ const Combobox = memo(
<Input
ref={inputRef}
className={cn(
'w-full pr-10 font-medium transition-colors',
'w-full pr-10 transition-colors',
(overlayContent || SelectedIcon) && 'text-transparent caret-foreground',
SelectedIcon && !overlayContent && 'pl-7',
open && 'focus-visible:border-[var(--border-1)]',
@@ -590,7 +590,7 @@ const Combobox = memo(
{(overlayContent || SelectedIcon) && (
<div
className={cn(
'pointer-events-none absolute top-0 right-[42px] bottom-0 left-0 flex items-center bg-transparent px-2 py-1.5 font-medium font-sans text-sm',
'pointer-events-none absolute top-0 right-[42px] bottom-0 left-0 flex items-center bg-transparent px-2 py-1.5 font-sans text-sm',
disabled && 'opacity-50'
)}
>
@@ -797,7 +797,7 @@ const Combobox = memo(
!option.disabled && setHighlightedIndex(globalIndex)
}
className={cn(
'relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-1.5 font-medium font-sans',
'relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-1.5 font-sans',
size === 'sm' ? 'py-[5px] text-caption' : 'py-1.5 text-sm',
'hover-hover:bg-[var(--surface-active)]',
(isHighlighted || isSelected) && 'bg-[var(--surface-active)]',
@@ -837,7 +837,7 @@ const Combobox = memo(
}}
onMouseEnter={() => setHighlightedIndex(-1)}
className={cn(
'relative flex cursor-pointer select-none items-center rounded-sm px-1.5 font-medium font-sans',
'relative flex cursor-pointer select-none items-center rounded-sm px-1.5 font-sans',
size === 'sm' ? 'py-[5px] text-caption' : 'py-1.5 text-sm',
'hover-hover:bg-[var(--surface-active)]',
!multiSelectValues?.length && 'bg-[var(--surface-active)]'
@@ -871,7 +871,7 @@ const Combobox = memo(
}}
onMouseEnter={() => !option.disabled && setHighlightedIndex(index)}
className={cn(
'relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-1.5 font-medium font-sans',
'relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-1.5 font-sans',
size === 'sm' ? 'py-[5px] text-caption' : 'py-1.5 text-sm',
'hover-hover:bg-[var(--surface-active)]',
(isHighlighted || isSelected) && 'bg-[var(--surface-active)]',
@@ -298,11 +298,7 @@ const DropdownMenuLabel = React.forwardRef<
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Label
ref={ref}
className={cn(
'px-2 py-1.5 font-medium text-[var(--text-tertiary)] text-xs',
inset && 'pl-7',
className
)}
className={cn('px-2 py-1.5 text-[var(--text-tertiary)] text-xs', inset && 'pl-7', className)}
{...props}
/>
))
@@ -77,7 +77,7 @@ const InputOTPSlot = React.forwardRef<
<div
ref={ref}
className={cn(
'relative flex h-12 w-12 items-center justify-center rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)] font-medium text-[var(--text-primary)] text-lg transition-colors',
'relative flex h-12 w-12 items-center justify-center rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)] text-[var(--text-primary)] text-lg transition-colors',
isActive && 'z-10 border-[var(--text-muted)] ring-1 ring-[var(--text-muted)]',
className
)}
+1 -1
View File
@@ -21,7 +21,7 @@ import * as React from 'react'
import { cn } from '../../lib/cn'
const INPUT_CLASS =
'flex w-full touch-manipulation rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)] px-2 py-1.5 font-medium font-sans text-sm text-[var(--text-primary)] transition-colors placeholder:text-[var(--text-muted)] outline-none disabled:cursor-not-allowed disabled:opacity-50 scroll-pr-1'
'flex w-full touch-manipulation rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)] px-2 py-1.5 font-sans text-sm text-[var(--text-primary)] transition-colors placeholder:text-[var(--text-muted)] outline-none disabled:cursor-not-allowed disabled:opacity-50 scroll-pr-1'
export type InputProps = React.InputHTMLAttributes<HTMLInputElement>
+1 -1
View File
@@ -25,7 +25,7 @@ function Label({ className, ...props }: LabelProps) {
return (
<LabelPrimitive.Root
className={cn(
'inline-flex items-center font-medium text-[var(--text-primary)] text-small leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50',
'inline-flex items-center text-[var(--text-primary)] text-small leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50',
className
)}
{...props}
+2 -2
View File
@@ -672,7 +672,7 @@ const ModalHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDi
className={cn('flex min-w-0 items-center justify-between gap-2 px-4 pt-4 pb-2', className)}
{...props}
>
<DialogPrimitive.Title className='min-w-0 font-medium text-[var(--text-primary)] text-base leading-none'>
<DialogPrimitive.Title className='min-w-0 text-[var(--text-primary)] text-base leading-none'>
{children}
</DialogPrimitive.Title>
<DialogPrimitive.Close asChild>
@@ -825,7 +825,7 @@ const ModalTabsTrigger = React.forwardRef<
<TabsPrimitive.Trigger
ref={ref}
className={cn(
'px-1 pb-2 font-medium text-[var(--text-secondary)] text-small transition-colors',
'px-1 pb-2 text-[var(--text-secondary)] text-small transition-colors',
'hover-hover:text-[var(--text-primary)] data-[state=active]:text-[var(--text-primary)]',
className
)}
@@ -837,7 +837,7 @@ const PopoverSection = React.forwardRef<HTMLDivElement, PopoverSectionProps>(
return (
<div
className={cn(
'mt-1.5 min-w-0 font-medium first:mt-0 first:pt-0',
'mt-1.5 min-w-0 first:mt-0 first:pt-0',
STYLES.colorScheme[colorScheme].section,
STYLES.size[size].section,
className
@@ -1114,13 +1114,7 @@ const PopoverBackButton = React.forwardRef<HTMLDivElement, PopoverBackButtonProp
</div>
)}
{folderTitle && !onFolderSelect && (
<div
className={cn(
'font-medium',
STYLES.colorScheme[colorScheme].section,
STYLES.size[size].section
)}
>
<div className={cn(STYLES.colorScheme[colorScheme].section, STYLES.size[size].section)}>
{folderTitle}
</div>
)}
@@ -1173,7 +1167,7 @@ const PopoverSearch = React.forwardRef<HTMLDivElement, PopoverSearchProps>(
<input
ref={inputRef}
className={cn(
'w-full bg-transparent font-medium focus:outline-none',
'w-full bg-transparent focus:outline-none',
STYLES.colorScheme[colorScheme].searchInput,
size === 'sm' ? 'text-xs' : 'text-caption'
)}
@@ -68,9 +68,7 @@ const ProgressItem = forwardRef<HTMLDivElement, ProgressItemProps>(function Prog
<StatusIcon status={status} />
<div className='flex min-w-0 flex-1 flex-col gap-0.5'>
<div className='flex items-center gap-2'>
<span className='min-w-0 flex-1 truncate font-medium text-[var(--text-primary)]'>
{title}
</span>
<span className='min-w-0 flex-1 truncate text-[var(--text-primary)]'>{title}</span>
{meta != null && (
<span className='shrink-0 text-[var(--text-secondary)] tabular-nums'>{meta}</span>
)}
@@ -180,7 +180,7 @@ function Tab({
aria-current={tab.active ? 'page' : undefined}
aria-label={tab.pinned ? tab.title : undefined}
className={cn(
'h-[30px] w-full select-none rounded-b-none border border-transparent border-b-0 bg-transparent py-0 font-normal text-caption',
'h-[30px] w-full select-none rounded-b-none border border-transparent border-b-0 bg-transparent py-0 text-caption',
tab.pinned ? 'justify-center px-0' : 'justify-start gap-1.5 px-2',
closeable && !tab.pinned && 'pr-7',
tab.active &&
+2 -2
View File
@@ -54,7 +54,7 @@ const TableFooter = React.forwardRef<
<tfoot
ref={ref}
className={cn(
'border-t bg-[color-mix(in_srgb,var(--surface-3)_50%,transparent)] font-medium [&>tr]:last:border-b-0',
'border-t bg-[color-mix(in_srgb,var(--surface-3)_50%,transparent)] [&>tr]:last:border-b-0',
className
)}
{...props}
@@ -80,7 +80,7 @@ const TableHead = React.forwardRef<
<th
ref={ref}
className={cn(
'h-10 px-3 py-2 text-left align-middle font-medium text-[var(--text-secondary)] [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
'h-10 px-3 py-2 text-left align-middle text-[var(--text-secondary)] [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
className
)}
{...props}
@@ -180,7 +180,7 @@ const TagInputTag = React.memo(function TagInputTag({
onRightIconClick={disabled ? undefined : handleRemove}
rightIconLabel={`Remove ${item.value}`}
>
<span className='min-w-0 flex-1 translate-y-[0.5px] truncate font-medium font-sans text-sm leading-5'>
<span className='min-w-0 flex-1 translate-y-[0.5px] truncate font-sans text-sm leading-5'>
{item.value}
</span>
{showError && <span className='sr-only'>{item.error}</span>}
@@ -423,7 +423,7 @@ const TagInput = React.forwardRef<HTMLInputElement, TagInputProps>(
<div className='relative inline-flex h-5 min-w-0 max-w-full items-center overflow-hidden'>
{inputValue.trim() && (
<span
className='invisible whitespace-pre font-medium font-sans text-sm leading-5'
className='invisible whitespace-pre font-sans text-sm leading-5'
aria-hidden='true'
>
{inputValue}
@@ -447,8 +447,8 @@ const TagInput = React.forwardRef<HTMLInputElement, TagInputProps>(
className={cn(
'appearance-none border-none bg-transparent align-middle font-sans outline-none placeholder:text-[var(--text-muted)] disabled:cursor-not-allowed disabled:opacity-50',
inputValue.trim()
? 'absolute top-0 left-0 h-full w-full p-0 font-medium text-inherit text-sm leading-5'
: 'h-5 w-auto min-w-0 p-0 font-medium text-[var(--text-body)] text-sm leading-5',
? 'absolute top-0 left-0 h-full w-full p-0 text-inherit text-sm leading-5'
: 'h-5 w-auto min-w-0 p-0 text-[var(--text-body)] text-sm leading-5',
inputClassName
)}
disabled={disabled}
@@ -3,7 +3,7 @@ import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '../../lib/cn'
const textareaVariants = cva(
'flex w-full touch-manipulation rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)] px-2 py-2 font-medium font-sans text-sm text-[var(--text-primary)] transition-colors placeholder:text-[var(--text-muted)] outline-none resize-none overflow-auto disabled:cursor-not-allowed disabled:opacity-50',
'flex w-full touch-manipulation rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)] px-2 py-2 font-sans text-sm text-[var(--text-primary)] transition-colors placeholder:text-[var(--text-muted)] outline-none resize-none overflow-auto disabled:cursor-not-allowed disabled:opacity-50',
{
variants: {
variant: {
@@ -36,7 +36,7 @@ import { Popover, PopoverAnchor, PopoverContent } from '../popover/popover'
* Matches the input and combobox styling patterns.
*/
const timePickerVariants = cva(
'flex w-full rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)] px-2 font-sans font-medium text-[var(--text-primary)] placeholder:text-[var(--text-muted)] outline-none disabled:cursor-not-allowed disabled:opacity-50 transition-colors',
'flex w-full rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)] px-2 font-sans text-[var(--text-primary)] placeholder:text-[var(--text-muted)] outline-none disabled:cursor-not-allowed disabled:opacity-50 transition-colors',
{
variants: {
variant: {
@@ -256,7 +256,7 @@ const TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(
<div className='flex items-center gap-1.5'>
<input
ref={hourInputRef}
className='w-[40px] rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)] px-1.5 py-[5px] text-center font-medium font-sans text-[var(--text-primary)] text-small outline-none transition-colors placeholder:text-[var(--text-muted)]'
className='w-[40px] rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)] px-1.5 py-[5px] text-center font-sans text-[var(--text-primary)] text-small outline-none transition-colors placeholder:text-[var(--text-muted)]'
value={hour}
onChange={handleHourChange}
onBlur={handleHourBlur}
@@ -266,9 +266,9 @@ const TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(
maxLength={2}
autoComplete='off'
/>
<span className='font-medium text-[var(--text-muted)] text-small'>:</span>
<span className='text-[var(--text-muted)] text-small'>:</span>
<input
className='w-[40px] rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)] px-1.5 py-[5px] text-center font-medium font-sans text-[var(--text-primary)] text-small outline-none transition-colors placeholder:text-[var(--text-muted)]'
className='w-[40px] rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)] px-1.5 py-[5px] text-center font-sans text-[var(--text-primary)] text-small outline-none transition-colors placeholder:text-[var(--text-muted)]'
value={minute}
onChange={handleMinuteChange}
onBlur={handleMinuteBlur}
@@ -288,7 +288,7 @@ const TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(
updateTime(undefined, undefined, period)
}}
className={cn(
'px-2 py-[5px] font-medium font-sans text-caption transition-colors',
'px-2 py-[5px] font-sans text-caption transition-colors',
ampm === period
? 'bg-[var(--brand-secondary)] text-[var(--bg)]'
: 'bg-[var(--surface-5)] text-[var(--text-secondary)] hover-hover:bg-[var(--surface-active)] hover-hover:text-[var(--text-primary)]'