mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
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:
+1
-4
@@ -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)}
|
||||
|
||||
+1
-4
@@ -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)}
|
||||
|
||||
+1
-1
@@ -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)]' />
|
||||
|
||||
@@ -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,
|
||||
|
||||
+1
-1
@@ -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>
|
||||
)
|
||||
|
||||
|
||||
+5
-4
@@ -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>
|
||||
|
||||
+3
-4
@@ -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>
|
||||
|
||||
+1
-2
@@ -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
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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)]'>
|
||||
|
||||
@@ -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
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
@@ -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)]'
|
||||
|
||||
Reference in New Issue
Block a user