Use the ui font-size tokens instead of raw px text utilities

text-[11px] and friends ignore the UI font size preference, which the repo's
font-scale contract test enforces. Same rendered size at the default scale.
This commit is contained in:
Unsloth 2026-07-25 20:30:08 -07:00
commit 5fd086326c
8 changed files with 46 additions and 46 deletions

View file

@ -344,7 +344,7 @@ export function DiffusionTrainDialog({
{uploading ? "Uploading..." : "Upload"}
</Button>
</div>
<p className="text-[11px] text-muted-foreground">
<p className="text-ui-11 text-muted-foreground">
10-50 images work well. Optional captions: a .txt per image (same
filename) or a metadata.jsonl; without them the trigger prompt below
captions every image. You can upload more into the same name later.
@ -352,7 +352,7 @@ export function DiffusionTrainDialog({
</div>
)}
{selectedDataset && selectedDataset.caption_count === 0 && (
<p className="text-[11px] text-muted-foreground">
<p className="text-ui-11 text-muted-foreground">
No caption files in this dataset - the trigger prompt below will be used
as the caption for every image.
</p>
@ -480,7 +480,7 @@ export function DiffusionTrainDialog({
: status.message}
{status.loss != null && !completed && <> · loss {status.loss.toFixed(4)}</>}
{status.lora_path && (
<div className="mt-1 break-all text-[11px]">Saved: {status.lora_path}</div>
<div className="mt-1 break-all text-ui-11">Saved: {status.lora_path}</div>
)}
</div>
</div>

View file

@ -512,7 +512,7 @@ function ResolvedBadge({
const resolved = status?.resolved?.[controlKey];
if (!resolved || resolved.source !== "auto") return null;
const badge = (
<span className="shrink-0 rounded-sm bg-muted px-1 py-px text-[9px] font-medium uppercase tracking-wider text-muted-foreground">
<span className="shrink-0 rounded-sm bg-muted px-1 py-px text-ui-9 font-medium uppercase tracking-wider text-muted-foreground">
Auto: {formatResolvedValue(controlKey, resolved.value)}
</span>
);
@ -567,7 +567,7 @@ function AdvancedSelect({
</SelectContent>
</Select>
</div>
{desc && <p className="text-[11px] leading-snug text-muted-foreground/70">{desc}</p>}
{desc && <p className="text-ui-11 leading-snug text-muted-foreground/70">{desc}</p>}
</div>
);
}
@ -949,7 +949,7 @@ function RecipePopover({
<PopoverContent align="end" side="top" className="w-80 p-0">
<div className="border-b border-border/60 px-4 py-2.5">
<p className="text-sm font-semibold">Generation settings</p>
<p className="text-[11px] text-muted-foreground">{formatTimestamp(image.created_at)}</p>
<p className="text-ui-11 text-muted-foreground">{formatTimestamp(image.created_at)}</p>
</div>
<div className="flex flex-col gap-2 px-4 py-3 text-xs">
<RecipeRow label="Prompt" value={image.prompt} wrap />

View file

@ -125,7 +125,7 @@ function LabelTile({
type="button"
onClick={remove}
disabled={deleting}
className="absolute right-1 top-1 rounded-md bg-background/80 px-1.5 py-0.5 text-[11px] text-muted-foreground opacity-0 transition-opacity hover:bg-background hover:text-destructive group-hover:opacity-100 focus:opacity-100"
className="absolute right-1 top-1 rounded-md bg-background/80 px-1.5 py-0.5 text-ui-11 text-muted-foreground opacity-0 transition-opacity hover:bg-background hover:text-destructive group-hover:opacity-100 focus:opacity-100"
>
{deleting ? "..." : "Remove"}
</button>
@ -140,10 +140,10 @@ function LabelTile({
rows={2}
spellCheck={false}
placeholder="Describe this image..."
className="min-h-[3rem] resize-none text-[11px]"
className="min-h-[3rem] resize-none text-ui-11"
aria-label={`Caption for ${record.filename}`}
/>
<div className="flex items-center justify-between text-[10px] text-muted-foreground">
<div className="flex items-center justify-between text-ui-10 text-muted-foreground">
<Tooltip>
<TooltipTrigger asChild={true}>
<span className="truncate">{record.filename}</span>
@ -214,7 +214,7 @@ export function DatasetLabelingGrid({
);
if (error) {
return <p className="text-[11px] text-destructive">{error}</p>;
return <p className="text-ui-11 text-destructive">{error}</p>;
}
if (records === null) {
return (
@ -224,7 +224,7 @@ export function DatasetLabelingGrid({
);
}
if (records.length === 0) {
return <p className="text-[11px] text-muted-foreground">This dataset has no images yet.</p>;
return <p className="text-ui-11 text-muted-foreground">This dataset has no images yet.</p>;
}
const uncaptioned = records.filter((r) => !r.caption || r.caption.trim() === "").length;
@ -236,7 +236,7 @@ export function DatasetLabelingGrid({
return (
<div className="flex flex-col gap-2">
<div className="flex items-center justify-between gap-2 text-[11px] text-muted-foreground">
<div className="flex items-center justify-between gap-2 text-ui-11 text-muted-foreground">
<span className="min-w-0 truncate">
{total} image{total === 1 ? "" : "s"}
{uncaptioned > 0 ? ` · ${uncaptioned} without a caption` : " · all captioned"}

View file

@ -112,7 +112,7 @@ export function DatasetShowcase({
className="hover-scrollbar flex w-full items-center gap-1.5 overflow-x-auto rounded-lg border border-border bg-muted/20 p-1.5 text-left transition-colors hover:border-foreground/20"
>
{names === null ? (
<div className="flex h-14 items-center gap-2 px-2 text-[11px] text-muted-foreground">
<div className="flex h-14 items-center gap-2 px-2 text-ui-11 text-muted-foreground">
<Spinner className="size-3.5" /> Loading preview...
</div>
) : (
@ -123,7 +123,7 @@ export function DatasetShowcase({
{remaining > 0 && (
<div className="flex size-14 shrink-0 flex-col items-center justify-center rounded-md bg-muted text-muted-foreground">
<span className="text-sm font-medium">+{remaining}</span>
<span className="text-[9px]">more</span>
<span className="text-ui-9">more</span>
</div>
)}
</>

View file

@ -125,7 +125,7 @@ export function DiffusionCharts({
showAvgLine={true}
scale="linear"
/>
<p className="px-1 text-[11px] leading-snug text-muted-foreground">
<p className="px-1 text-ui-11 leading-snug text-muted-foreground">
Per-step loss is noisy by design: every step samples a random noise level. Watch
the smoothed line for the trend, not the raw jitter.
</p>

View file

@ -910,7 +910,7 @@ export function DiffusionTrainPanel({
<SelectItem value="linear">Linear decay</SelectItem>
</SelectContent>
</Select>
<p className="text-[11px] leading-snug text-muted-foreground">
<p className="text-ui-11 leading-snug text-muted-foreground">
Constant is fine for most runs.
</p>
</div>
@ -933,7 +933,7 @@ export function DiffusionTrainPanel({
<SelectItem value="off">Off (faster steps)</SelectItem>
</SelectContent>
</Select>
<p className="text-[11px] leading-snug text-muted-foreground">
<p className="text-ui-11 leading-snug text-muted-foreground">
Less VRAM, slightly slower steps.
</p>
</div>
@ -964,7 +964,7 @@ export function DiffusionTrainPanel({
))}
</SelectContent>
</Select>
<p className="text-[11px] leading-snug text-muted-foreground">
<p className="text-ui-11 leading-snug text-muted-foreground">
{familyUntrainable ? (
// The reason itself (the backend's bf16-preflight text) already shows in
// the family picker's vram_note line above.
@ -995,7 +995,7 @@ export function DiffusionTrainPanel({
<SelectItem value="no">fp32 (no mixed)</SelectItem>
</SelectContent>
</Select>
<p className="text-[11px] leading-snug text-muted-foreground">
<p className="text-ui-11 leading-snug text-muted-foreground">
bf16 is right for modern GPUs.
</p>
</div>
@ -1018,7 +1018,7 @@ export function DiffusionTrainPanel({
<SelectItem value="off">Off</SelectItem>
</SelectContent>
</Select>
<p className="text-[11px] leading-snug text-muted-foreground">
<p className="text-ui-11 leading-snug text-muted-foreground">
Slower first step, faster after.
</p>
</div>
@ -1036,7 +1036,7 @@ export function DiffusionTrainPanel({
<div className="hover-scrollbar flex min-h-0 flex-col gap-5 overflow-y-auto overflow-x-hidden pb-7 pl-0.5 pr-7">
<div>
<h2 className="text-base font-semibold">Train a LoRA</h2>
<p className="mt-1 text-[11px] leading-snug text-muted-foreground">
<p className="mt-1 text-ui-11 leading-snug text-muted-foreground">
Teach a model a style or subject from your own images.
</p>
</div>
@ -1057,7 +1057,7 @@ export function DiffusionTrainPanel({
</SelectContent>
</Select>
{family?.vram_note && (
<p className="text-[11px] leading-snug text-muted-foreground">{family.vram_note}</p>
<p className="text-ui-11 leading-snug text-muted-foreground">{family.vram_note}</p>
)}
</div>
@ -1127,7 +1127,7 @@ export function DiffusionTrainPanel({
</SelectContent>
</Select>
{importingId && (
<p className="text-[11px] text-muted-foreground">
<p className="text-ui-11 text-muted-foreground">
Importing {examples.find((e) => e.id === importingId)?.label ?? "example"}...
</p>
)}
@ -1170,7 +1170,7 @@ export function DiffusionTrainPanel({
{/* Count and Upload appear only once files are picked. */}
{pickedFileCount > 0 && (
<>
<span className="min-w-0 flex-1 truncate text-[11px] text-muted-foreground">
<span className="min-w-0 flex-1 truncate text-ui-11 text-muted-foreground">
{pickedFileCount} file{pickedFileCount === 1 ? "" : "s"} selected
</span>
<Button
@ -1186,7 +1186,7 @@ export function DiffusionTrainPanel({
</>
)}
</div>
<p className="text-[11px] leading-snug text-muted-foreground">
<p className="text-ui-11 leading-snug text-muted-foreground">
10-50 images is plenty. Captions are optional.
</p>
</div>
@ -1214,7 +1214,7 @@ export function DiffusionTrainPanel({
/>
)}
{selectedDataset.caption_count === 0 && !gridOpen && (
<p className="text-[11px] leading-snug text-muted-foreground">
<p className="text-ui-11 leading-snug text-muted-foreground">
No captions yet, so the trigger prompt describes every image.
</p>
)}
@ -1232,7 +1232,7 @@ export function DiffusionTrainPanel({
{/* Trigger + adapter name (trigger first: it describes the dataset, the name
just labels the output) */}
{fullyCaptioned ? (
<p className="text-[11px] leading-snug text-muted-foreground">
<p className="text-ui-11 leading-snug text-muted-foreground">
All {selectedDataset?.image_count} images have captions, so no trigger prompt
is needed.
</p>
@ -1245,7 +1245,7 @@ export function DiffusionTrainPanel({
onChange={(e) => setInstancePrompt(e.target.value)}
className="h-8 text-xs"
/>
<p className="text-[11px] leading-snug text-muted-foreground">
<p className="text-ui-11 leading-snug text-muted-foreground">
The words you will use later to get this style back.
</p>
</div>
@ -1259,7 +1259,7 @@ export function DiffusionTrainPanel({
onChange={(e) => setOutputDir(e.target.value)}
className="h-8 text-xs"
/>
<p className="text-[11px] leading-snug text-muted-foreground">
<p className="text-ui-11 leading-snug text-muted-foreground">
Its name in the Create tab&apos;s picker.
</p>
</div>
@ -1323,7 +1323,7 @@ export function DiffusionTrainPanel({
}
/>
</div>
<p className="text-[11px] text-muted-foreground">
<p className="text-ui-11 text-muted-foreground">
{viewRun.family ? `${viewRun.family} - ` : ""}
{viewRun.base_model || ""}
{viewRun.ended_at
@ -1367,7 +1367,7 @@ export function DiffusionTrainPanel({
</span>
</div>
{trainingSettings}
<p className="text-[11px] leading-snug text-muted-foreground">
<p className="text-ui-11 leading-snug text-muted-foreground">
Progress and charts take over here once training starts.
</p>
</div>
@ -1394,14 +1394,14 @@ export function DiffusionTrainPanel({
</span>
<span className="flex shrink-0 items-center gap-2">
{r.saved && (
<span className="rounded-full bg-primary/15 px-2 py-0.5 text-[10px] text-primary">
<span className="rounded-full bg-primary/15 px-2 py-0.5 text-ui-10 text-primary">
adapter saved
</span>
)}
<span className="text-[10px] uppercase tracking-wide text-muted-foreground">
<span className="text-ui-10 uppercase tracking-wide text-muted-foreground">
{r.status}
</span>
<span className="text-[10px] text-muted-foreground">
<span className="text-ui-10 text-muted-foreground">
{r.ended_at ? new Date(r.ended_at * 1000).toLocaleString() : ""}
</span>
</span>
@ -1457,7 +1457,7 @@ export function DiffusionTrainPanel({
/>
</div>
{status?.message && (
<p className="text-[11px] text-muted-foreground">{status.message}</p>
<p className="text-ui-11 text-muted-foreground">{status.message}</p>
)}
{running && (
<Button
@ -1493,7 +1493,7 @@ export function DiffusionTrainPanel({
<span className="text-sm font-semibold">
{completed ? "Adapter ready" : "Partial adapter saved"}
</span>
<p className="text-[11px] text-muted-foreground">
<p className="text-ui-11 text-muted-foreground">
{completed
? "Trained"
: "Stopped early; the adapter as of the last finished step was saved"}
@ -1556,7 +1556,7 @@ export function DiffusionTrainPanel({
function Stat({ label, value }: { label: string; value: string }) {
return (
<div className={cn("rounded-lg border border-border/60 bg-muted/20 px-2.5 py-1.5")}>
<div className="text-[10px] uppercase tracking-wide text-muted-foreground">{label}</div>
<div className="text-ui-10 uppercase tracking-wide text-muted-foreground">{label}</div>
<div className="text-sm font-medium tabular-nums">{value}</div>
</div>
);

View file

@ -103,7 +103,7 @@ export function ExampleDatasetCards({
return (
<div className="grid gap-2">
<span className="text-[11px] font-medium text-muted-foreground">
<span className="text-ui-11 font-medium text-muted-foreground">
Or start from an example dataset
</span>
<div className="grid gap-2">
@ -124,14 +124,14 @@ export function ExampleDatasetCards({
</Tooltip>
<Tooltip>
<TooltipTrigger asChild={true}>
<span className="max-w-[110px] shrink truncate rounded-full bg-secondary px-2 py-0.5 text-[10px] font-normal text-secondary-foreground">
<span className="max-w-[110px] shrink truncate rounded-full bg-secondary px-2 py-0.5 text-ui-10 font-normal text-secondary-foreground">
{ex.license}
</span>
</TooltipTrigger>
<TooltipContent>{ex.license}</TooltipContent>
</Tooltip>
</div>
<p className="line-clamp-2 text-[11px] leading-snug text-muted-foreground">
<p className="line-clamp-2 text-ui-11 leading-snug text-muted-foreground">
{ex.description}
</p>
{/* The card's action, so: bottom right, on the thumbnail row. */}

View file

@ -344,7 +344,7 @@ function ResolvedBadge({
const resolved = status?.resolved?.[controlKey];
if (!resolved || resolved.source !== "auto") return null;
const badge = (
<span className="shrink-0 rounded-sm bg-muted px-1 py-px text-[9px] font-medium uppercase tracking-wider text-muted-foreground">
<span className="shrink-0 rounded-sm bg-muted px-1 py-px text-ui-9 font-medium uppercase tracking-wider text-muted-foreground">
Auto: {formatResolvedValue(resolved.value)}
</span>
);
@ -435,7 +435,7 @@ function RecipePopover({
<PopoverContent align="end" side="top" className="w-80 p-0">
<div className="border-b border-border/60 px-4 py-2.5">
<p className="text-sm font-semibold">Generation settings</p>
<p className="text-[11px] text-muted-foreground">{formatTimestamp(video.created_at)}</p>
<p className="text-ui-11 text-muted-foreground">{formatTimestamp(video.created_at)}</p>
</div>
<div className="flex flex-col gap-2 px-4 py-3 text-xs">
<RecipeRow label="Prompt" value={video.prompt} wrap />
@ -1401,7 +1401,7 @@ export function VideoPage({ active = true }: { active?: boolean }) {
{/* Loaded-model status line: family / kind / offload / speed, like the images page
surfaces on load. Hidden until a model is resident. */}
{status?.loaded && (
<div className="hidden items-center gap-3 text-[11px] md:flex">
<div className="hidden items-center gap-3 text-ui-11 md:flex">
{status.family && <StatusChip label="Family" value={status.family} />}
{status.model_kind && <StatusChip label="Kind" value={status.model_kind} />}
{status.offload_policy && (
@ -1584,7 +1584,7 @@ export function VideoPage({ active = true }: { active?: boolean }) {
className="max-h-full max-w-full rounded-xl object-contain shadow-sm"
/>
{selected.has_audio && (
<div className="absolute left-4 top-4 flex items-center gap-1 rounded-lg bg-background/80 px-2 py-1 text-[11px] font-medium shadow-lg ring-1 ring-border backdrop-blur">
<div className="absolute left-4 top-4 flex items-center gap-1 rounded-lg bg-background/80 px-2 py-1 text-ui-11 font-medium shadow-lg ring-1 ring-border backdrop-blur">
<HugeiconsIcon icon={VolumeHighIcon} className="size-3.5" />
Audio
</div>
@ -1717,7 +1717,7 @@ export function VideoPage({ active = true }: { active?: boolean }) {
{/* A terse caption strip so cards read at a glance. Left/bottom
padding clears the rounded-lg corner and the selection border
so the leading "5.0s" is never clipped by the curve. */}
<span className="relative z-10 truncate bg-gradient-to-t from-black/70 to-transparent px-2 pb-1 pt-2 text-left text-[9px] font-medium leading-none text-white">
<span className="relative z-10 truncate bg-gradient-to-t from-black/70 to-transparent px-2 pb-1 pt-2 text-left text-ui-9 font-medium leading-none text-white">
{clipMeta(video)}
</span>
{/* Selection marker on a non-focusable overlay. */}
@ -1750,7 +1750,7 @@ export function VideoPage({ active = true }: { active?: boolean }) {
className="flex h-16 w-16 shrink-0 flex-col items-center justify-center gap-1 rounded-[10px] text-muted-foreground ring-1 ring-border transition-colors hover:text-destructive hover:ring-destructive/40"
>
<HugeiconsIcon icon={Delete02Icon} className="size-4" />
<span className="text-[9px] font-medium">Clear all</span>
<span className="text-ui-9 font-medium">Clear all</span>
</button>
</TooltipTrigger>
<TooltipContent>Clear all videos</TooltipContent>