Readable Text
weekendsuperhero-io-sistine/readable-textFreeComponent
APCA contrast guard: renders text in an accent color while it stays readable on the current glass surface, and swaps to a readable foreground otherwise. Reactive to the live glass tint and light/dark.
Install it
npx shadcn@latest add https://raw.githubusercontent.com/Weekendsuperhero-io/sistine/main/public/r/readable-text.jsonSource
1"use client";23import * as React from "react";4import {5 apcaContrast,6 formatOklch,7 glassSurface,8 type OklchColor,9 parseOklch,10 READABLE_USAGE,11 type ReadableUsage,12 readableForeground,13} from "@/lib/oklch-utils";1415/**16 * Readable foreground for a KNOWN surface color — pure and memoized (no DOM read, no observer), so it17 * scales to hundreds of off-theme surfaces (e.g. colored tool-call bubbles) at negligible cost.18 * Returns an oklch() string banded for `usage` (ARC Bronze; default "body"). For theme-surface text,19 * prefer the CSS tokens (text-foreground / -soft / -strong) or <ReadableText>.20 *21 * const fg = useReadableForeground({ l: 70, c: 0.18, h: 50 }, "ui"); // readable icon on orange22 */23export function useReadableForeground(surface: string | OklchColor, usage: ReadableUsage = "body"): string {24 const css = typeof surface === "string" ? surface : formatOklch(surface);25 return React.useMemo(() => {26 const s = parseOklch(css);27 return s28 ? formatOklch(29 readableForeground(s, {30 usage,31 }),32 )33 : "inherit";34 }, [35 css,36 usage,37 ]);38}3940const cssNum = (cs: CSSStyleDeclaration, name: string, fallback: number): number => {41 const v = Number.parseFloat(cs.getPropertyValue(name));42 return Number.isNaN(v) ? fallback : v;43};4445/**46 * Dynamic APCA contrast guard. Renders its text in `accent` (a CSS custom-property name such as47 * "--primary") *while that color stays readable* on the surface, otherwise a soft readable foreground48 * via readableForeground — legible without the pure-black/white spike. The surface is the live glass49 * (reactive to tint + light/dark) by default; pass `on` (an oklch string) to band against an arbitrary50 * surface, e.g. an off-theme bubble. `usage` selects the ARC-Bronze band. Reuses lib/oklch-utils.51 */52export function ReadableText({53 accent,54 on,55 usage,56 minLc,57 className,58 children,59}: {60 /** CSS custom-property name to use when readable, e.g. "--primary". */61 accent: string;62 /** Surface to judge against — an oklch() string. Defaults to the live glass surface. */63 on?: string;64 /** ARC-Bronze band ("body" | "large" | "ui" | …) — sets the keep-accent floor + the soft fallback. */65 usage?: ReadableUsage;66 /** Minimum |APCA Lc| to keep the accent. Defaults to the usage floor, else 45. */67 minLc?: number;68 className?: string;69 children: React.ReactNode;70}) {71 const [color, setColor] = React.useState<string>();7273// … truncated
What it pulls in
Other registry items
Files it writes
More from weekendsuperhero-io/sistine
All 71 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | weekendsuperhero-io/sistine | Components | Free | 5 deps · 4 files | |
| Alertalert | weekendsuperhero-io/sistine | Components | Free | 3 deps · 4 files | |
| Alert Dialogalert-dialog | weekendsuperhero-io/sistine | Components | Free | 4 deps · 4 files | |
| Auto Foregroundauto-foreground | weekendsuperhero-io/sistine | Components | Free | no deps | |
| Avataravatar | weekendsuperhero-io/sistine | Components | Free | 4 deps · 4 files | |
| Badgebadge | weekendsuperhero-io/sistine | Components | Free | 4 deps · 4 files | |
| Breadcrumbbreadcrumb | weekendsuperhero-io/sistine | Components | Free | 5 deps · 4 files | |
| Buttonbutton | weekendsuperhero-io/sistine | Components | Free | 4 deps · 4 files |
