refactor(web): migrate feature screens to design tokens + radius token (#49)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-07 14:15:54 +02:00
parent 04ed0c50e2
commit cde7be9f2a
24 changed files with 90 additions and 90 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ const FieldsPage = lazy(() =>
);
function FormFallback() {
return <div role="status" className="p-4 text-sm text-neutral-400">Loading</div>;
return <div role="status" className="p-4 text-sm text-muted-foreground">Loading</div>;
}
export function App() {
+1 -1
View File
@@ -53,7 +53,7 @@ export function LoginPage() {
/>
</div>
{errorKey && (
<p role="alert" className="text-sm text-red-600">
<p role="alert" className="text-sm text-destructive">
{t(errorKey)}
</p>
)}
+6 -6
View File
@@ -53,7 +53,7 @@ export function AuthoritiesPage() {
role="tab"
aria-selected={k === currentKind}
className={({ isActive }) =>
`rounded px-3 py-1 text-sm ${isActive ? "bg-neutral-800 text-white" : "border"}`
`rounded-md px-3 py-1 text-sm ${isActive ? "bg-primary text-primary-foreground" : "border"}`
}
>
{t(`authorities.${k}`)}
@@ -63,13 +63,13 @@ export function AuthoritiesPage() {
<ul className="mb-4">
{isLoading && (
<li className="text-sm text-neutral-400"></li>
<li className="text-sm text-muted-foreground"></li>
)}
{isError && (
<li className="text-sm text-red-600">{t("authorities.loadError")}</li>
<li className="text-sm text-destructive">{t("authorities.loadError")}</li>
)}
{!isLoading && !isError && authorities?.length === 0 && (
<li className="text-sm text-neutral-500">{t("authorities.empty")}</li>
<li className="text-sm text-muted-foreground">{t("authorities.empty")}</li>
)}
{authorities?.map((a) => (
<AuthorityRow key={a.id} authority={a} kind={currentKind} lang={lang} />
@@ -84,13 +84,13 @@ export function AuthoritiesPage() {
<LabelEditor value={labels} onChange={setLabels} />
{error && (
<p role="alert" className="text-xs text-red-600">
<p role="alert" className="text-xs text-destructive">
{t("form.required")}
</p>
)}
{create.isError && (
<p role="alert" className="text-xs text-red-600">
<p role="alert" className="text-xs text-destructive">
{t("form.rejected")}
</p>
)}
+2 -2
View File
@@ -47,7 +47,7 @@ export function DeleteConfirmDialog({
<AlertDialog open={open} onOpenChange={setOpen}>
<AlertDialogTrigger
render={
<Button variant="ghost" size="sm" className="text-red-600">
<Button variant="ghost" size="sm" className="text-destructive">
{triggerLabel ?? t("actions.delete")}
</Button>
}
@@ -56,7 +56,7 @@ export function DeleteConfirmDialog({
<AlertDialogTitle>{t("actions.delete")}</AlertDialogTitle>
<AlertDialogDescription>{description}</AlertDialogDescription>
{message && (
<p role="alert" className="text-sm text-red-600">
<p role="alert" className="text-sm text-destructive">
{message}
</p>
)}
+5 -5
View File
@@ -122,7 +122,7 @@ export function FieldForm({
value={dataType}
disabled={isEdit}
onChange={(e) => setDataType(e.target.value)}
className="w-full rounded border px-2 py-1 text-sm disabled:opacity-60"
className="w-full rounded-md border px-2 py-1 text-sm disabled:opacity-60"
>
{TYPES.map((type) => (
<option key={type} value={type}>
@@ -140,7 +140,7 @@ export function FieldForm({
value={vocabularyId}
disabled={isEdit}
onChange={(e) => setVocabularyId(e.target.value)}
className="w-full rounded border px-2 py-1 text-sm disabled:opacity-60"
className="w-full rounded-md border px-2 py-1 text-sm disabled:opacity-60"
>
<option value="">{t("form.selectPlaceholder")}</option>
{vocabularies?.map((vocab) => (
@@ -160,7 +160,7 @@ export function FieldForm({
value={authorityKind}
disabled={isEdit}
onChange={(e) => setAuthorityKind(e.target.value)}
className="w-full rounded border px-2 py-1 text-sm disabled:opacity-60"
className="w-full rounded-md border px-2 py-1 text-sm disabled:opacity-60"
>
<option value="">{t("fields.anyKind")}</option>
{KINDS.map((kind) => (
@@ -183,12 +183,12 @@ export function FieldForm({
</label>
{error && (
<p role="alert" className="text-xs text-red-600">
<p role="alert" className="text-xs text-destructive">
{t("form.required")}
</p>
)}
{failed && (
<p role="alert" className="text-xs text-red-600">
<p role="alert" className="text-xs text-destructive">
{t("form.rejected")}
</p>
)}
+7 -7
View File
@@ -30,9 +30,9 @@ export function FieldList({
);
}
if (isError) return <p className="p-4 text-sm text-red-600">{t("fields.loadError")}</p>;
if (isError) return <p className="p-4 text-sm text-destructive">{t("fields.loadError")}</p>;
if (!data || data.length === 0)
return <p className="p-4 text-sm text-neutral-500">{t("fields.empty")}</p>;
return <p className="p-4 text-sm text-muted-foreground">{t("fields.empty")}</p>;
const groups = new Map<string, FieldDefinitionView[]>();
@@ -53,7 +53,7 @@ export function FieldList({
<ul className="overflow-auto">
{entries.map(([group, defs]) => (
<li key={group}>
<div className="border-b bg-neutral-50 px-3 py-1 text-xs font-medium uppercase tracking-wide text-neutral-500">
<div className="border-b bg-muted px-3 py-1 label-caption">
{group}
</div>
<ul>
@@ -61,7 +61,7 @@ export function FieldList({
<li
key={def.key}
className={`flex items-center gap-2 border-b px-3 py-2 text-sm ${
def.key === selectedKey ? "bg-indigo-50" : ""
def.key === selectedKey ? "bg-primary/10" : ""
}`}
>
<button
@@ -71,13 +71,13 @@ export function FieldList({
onClick={() => onSelect(def)}
>
<span className="font-medium">{labelText(def.labels, lang)}</span>
<span className="text-xs text-neutral-400">{def.key}</span>
<span className="rounded bg-neutral-100 px-1.5 py-0.5 text-xs text-neutral-600">
<span className="text-xs text-muted-foreground">{def.key}</span>
<span className="rounded-md bg-muted px-1.5 py-0.5 text-xs text-muted-foreground">
{t(`fields.types.${def.data_type}`)}
</span>
{def.required && (
<span
className="text-xs text-red-600"
className="text-xs text-destructive"
title={t("fields.required")}
aria-label={t("fields.required")}
>
+2 -2
View File
@@ -39,7 +39,7 @@ export function DeleteObjectDialog({ id }: { id: string }) {
<AlertDialog open={open} onOpenChange={setOpen}>
<AlertDialogTrigger
render={
<Button variant="ghost" size="sm" className="text-red-600">
<Button variant="ghost" size="sm" className="text-destructive">
{t("actions.delete")}
</Button>
}
@@ -49,7 +49,7 @@ export function DeleteObjectDialog({ id }: { id: string }) {
<AlertDialogTitle>{t("actions.delete")}</AlertDialogTitle>
<AlertDialogDescription>{t("actions.confirmDelete")}</AlertDialogDescription>
{error && (
<p role="alert" className="text-sm text-red-600">
<p role="alert" className="text-sm text-destructive">
{t("form.rejected")}
</p>
)}
+4 -4
View File
@@ -49,9 +49,9 @@ function TermValue({
if (typeof value !== "string") return <></>;
const term = terms?.find((x) => x.id === value);
if (term) return <>{labelText(term.labels, lang)}</>;
if (isLoading) return <span className="text-neutral-400"></span>;
if (isLoading) return <span className="text-muted-foreground"></span>;
return (
<span className="text-neutral-400">
<span className="text-muted-foreground">
{value} {t("objects.unknownRef")}
</span>
);
@@ -72,9 +72,9 @@ function AuthorityValue({
if (typeof value !== "string") return <></>;
const authority = authorities?.find((x) => x.id === value);
if (authority) return <>{labelText(authority.labels, lang)}</>;
if (isLoading) return <span className="text-neutral-400"></span>;
if (isLoading) return <span className="text-muted-foreground"></span>;
return (
<span className="text-neutral-400">
<span className="text-muted-foreground">
{value} {t("objects.unknownRef")}
</span>
);
+1 -1
View File
@@ -30,7 +30,7 @@ export function ObjectDetailDrawer({
<div className="flex justify-end border-b p-2">
<DrawerClose
aria-label={t("actions.closeDetail")}
className="rounded p-1 text-neutral-500 hover:bg-neutral-100 hover:text-neutral-900"
className="rounded-md p-1 text-muted-foreground hover:bg-muted hover:text-foreground"
>
<X className="size-4" aria-hidden="true" />
</DrawerClose>
+6 -6
View File
@@ -19,8 +19,8 @@ function Field({ label, value }: { label: string; value: ReactNode }) {
return (
<div className="border-b py-2">
<div className="text-xs uppercase tracking-wide text-neutral-400">{label}</div>
<div className="text-sm text-neutral-900">{empty ? "—" : value}</div>
<div className="label-caption">{label}</div>
<div className="text-sm text-foreground">{empty ? "—" : value}</div>
</div>
);
}
@@ -39,9 +39,9 @@ export function ObjectDetail() {
);
}
if (isError) return <p className="p-4 text-sm text-red-600">{t("objects.loadError")}</p>;
if (isError) return <p className="p-4 text-sm text-destructive">{t("objects.loadError")}</p>;
if (!object) return <p className="p-4 text-sm text-neutral-500">{t("objects.notFound")}</p>;
if (!object) return <p className="p-4 text-sm text-muted-foreground">{t("objects.notFound")}</p>;
// Prefer the active locale's label, then English, then the raw key.
const lang = i18n.language.startsWith("sv") ? "sv" : "en";
@@ -105,7 +105,7 @@ export function ObjectDetail() {
/>
{groups.map((g) => (
<div key={g.group} className="mt-4">
<div className="mb-1 text-xs font-medium uppercase text-neutral-500">{g.group}</div>
<div className="mb-1 label-caption">{g.group}</div>
{g.defs.map((d) => (
<Field
key={d.key}
@@ -119,7 +119,7 @@ export function ObjectDetail() {
key={key}
label={key}
value={
<span className="text-neutral-400">
<span className="text-muted-foreground">
{typeof value === "object" ? JSON.stringify(value) : String(value)}
</span>
}
+1 -1
View File
@@ -29,7 +29,7 @@ export function ObjectEditForm() {
if (isLoading) return <div className="p-4" role="status" aria-label="loading" />;
if (!object) return <p className="p-4 text-sm text-neutral-500">{t("objects.notFound")}</p>;
if (!object) return <p className="p-4 text-sm text-muted-foreground">{t("objects.notFound")}</p>;
const core: ObjectCore = {
object_number: object.object_number,
+5 -5
View File
@@ -117,7 +117,7 @@ export function ObjectForm({
/>
{errors.core?.[key] && (
<p role="alert" className="text-xs text-red-600">
<p role="alert" className="text-xs text-destructive">
{t("form.required")}
</p>
)}
@@ -127,7 +127,7 @@ export function ObjectForm({
return (
<form onSubmit={submit} className="space-y-4 overflow-auto p-4">
{formError && (
<p role="alert" className="text-sm text-red-600">
<p role="alert" className="text-sm text-destructive">
{formError}
</p>
)}
@@ -147,7 +147,7 @@ export function ObjectForm({
<select
id="visibility"
className="w-full rounded border px-2 py-1 text-sm"
className="w-full rounded-md border px-2 py-1 text-sm"
{...register("visibility")}
>
<option value="draft">{t("form.draft")}</option>
@@ -158,7 +158,7 @@ export function ObjectForm({
{definitions && definitions.length > 0 && (
<fieldset className="space-y-3 border-t pt-3">
<legend className="text-xs font-medium uppercase text-neutral-500">
<legend className="label-caption">
{t("form.flexibleHeading")}
</legend>
@@ -167,7 +167,7 @@ export function ObjectForm({
<FieldInput definition={def} form={form} />
{errors.fields?.[def.key] && (
<p role="alert" className="text-xs text-red-600">
<p role="alert" className="text-xs text-destructive">
{errors.fields[def.key]?.message ?? t("form.required")}
</p>
)}
+1 -1
View File
@@ -42,7 +42,7 @@ export function ObjectsPage() {
type="button"
onClick={closeDetail}
aria-label={t("actions.closeDetail")}
className="rounded p-1 text-neutral-500 hover:bg-neutral-100 hover:text-neutral-900"
className="rounded-md p-1 text-muted-foreground hover:bg-muted hover:text-foreground"
>
<X className="size-4" aria-hidden="true" />
</button>
+12 -12
View File
@@ -138,10 +138,10 @@ export function ObjectsTable() {
<button
type="button"
onClick={() => toggleSort(col)}
className="flex items-center gap-1 hover:text-neutral-900"
className="flex items-center gap-1 hover:text-foreground"
>
{t(COLUMN_KEYS[col])}
<Icon className="size-3.5 text-neutral-400" aria-hidden="true" />
<Icon className="size-3.5 text-muted-foreground" aria-hidden="true" />
</button>
</th>
);
@@ -170,7 +170,7 @@ export function ObjectsTable() {
type="button"
aria-pressed={active}
onClick={() => setVisibility(value)}
className={`rounded px-2 py-1 ${active ? "bg-indigo-600 text-white" : "border"}`}
className={`rounded-md px-2 py-1 ${active ? "bg-primary text-primary-foreground" : "border"}`}
>
{value === "all" ? t("search.all") : t(`visibility.${value}`)}
</button>
@@ -184,7 +184,7 @@ export function ObjectsTable() {
);
const columns = (
<thead className="border-b bg-neutral-50 text-xs text-neutral-500">
<thead className="border-b bg-muted text-xs text-muted-foreground">
<tr>
{headerCell("object_number")}
{headerCell("object_name")}
@@ -220,7 +220,7 @@ export function ObjectsTable() {
body = (
<tbody>
<tr>
<td colSpan={6} className="px-3 py-6 text-center text-sm text-red-600">
<td colSpan={6} className="px-3 py-6 text-center text-sm text-destructive">
{t("objects.loadError")}
</td>
</tr>
@@ -230,7 +230,7 @@ export function ObjectsTable() {
body = (
<tbody>
<tr>
<td colSpan={6} className="px-3 py-6 text-center text-sm text-neutral-500">
<td colSpan={6} className="px-3 py-6 text-center text-sm text-muted-foreground">
{t("objects.empty")}
</td>
</tr>
@@ -254,17 +254,17 @@ export function ObjectsTable() {
if (event.key === "Enter") navigate(`/objects/${object.id}?${params}`);
}}
className={`cursor-pointer border-b text-sm ${
selected ? "bg-indigo-50" : "hover:bg-neutral-50"
selected ? "bg-primary/10" : "hover:bg-muted"
}`}
>
<td className="px-3 py-2 text-neutral-500">{object.object_number}</td>
<td className="px-3 py-2 text-muted-foreground">{object.object_number}</td>
<td className="px-3 py-2 font-medium">{object.object_name}</td>
<td className="px-3 py-2">
<VisibilityBadge visibility={object.visibility} />
</td>
<td className="px-3 py-2 text-neutral-600">{object.current_location ?? "—"}</td>
<td className="px-3 py-2 text-muted-foreground">{object.current_location ?? "—"}</td>
<td className="px-3 py-2 text-right tabular-nums">{object.number_of_objects}</td>
<td className="px-3 py-2 text-neutral-600">{formatUpdated(object.updated_at)}</td>
<td className="px-3 py-2 text-muted-foreground">{formatUpdated(object.updated_at)}</td>
</tr>
);
})}
@@ -281,14 +281,14 @@ export function ObjectsTable() {
{body}
</table>
</div>
<div className="flex items-center justify-between gap-2 border-t px-3 py-2 text-xs text-neutral-500">
<div className="flex items-center justify-between gap-2 border-t px-3 py-2 text-xs text-muted-foreground">
<label className="flex items-center gap-1">
<span>{t("objects.pageSize")}</span>
<select
value={limit}
onChange={(event) => setLimit(Number(event.target.value))}
aria-label={t("objects.pageSize")}
className="rounded border bg-white px-1 py-0.5"
className="rounded-md border bg-white px-1 py-0.5"
>
{PAGE_SIZES.map((size) => (
<option key={size} value={size}>
+1 -1
View File
@@ -53,7 +53,7 @@ export function OptionsCombobox({
<ComboboxList>
{(option: Option) => (
<ComboboxItem key={option.id} value={option}>
<ComboboxItemIndicator className="text-indigo-600"></ComboboxItemIndicator>
<ComboboxItemIndicator className="text-primary"></ComboboxItemIndicator>
{labelText(option.labels, lang)}
</ComboboxItem>
)}
+7 -7
View File
@@ -48,7 +48,7 @@ export function PublishControl({ object }: { object: AdminObjectView }) {
return (
<section className="border-t p-4">
<div className="mb-2 text-xs font-medium uppercase text-neutral-500">
<div className="mb-2 label-caption">
{t("publish.heading")}
</div>
@@ -59,10 +59,10 @@ export function PublishControl({ object }: { object: AdminObjectView }) {
aria-current={i === currentIndex ? "step" : undefined}
className={`flex-1 border px-2 py-1 text-center text-xs ${
i === currentIndex
? "bg-neutral-800 font-semibold text-white"
? "bg-primary font-semibold text-primary-foreground"
: i < currentIndex
? "bg-neutral-100 text-neutral-600"
: "text-neutral-400"
? "bg-muted text-muted-foreground"
: "text-muted-foreground"
}`}
>
{t(`visibility.${step}`)}
@@ -117,7 +117,7 @@ export function PublishControl({ object }: { object: AdminObjectView }) {
</div>
{errorKind === "gate" && (
<p role="alert" className="mt-2 text-sm text-red-600">
<p role="alert" className="mt-2 text-sm text-destructive">
{t("publish.gateError")}{" "}
<Link to={`/objects/${object.id}/edit`} className="underline">
{t("publish.editLink")}
@@ -125,12 +125,12 @@ export function PublishControl({ object }: { object: AdminObjectView }) {
</p>
)}
{errorKind === "illegal" && (
<p role="alert" className="mt-2 text-sm text-red-600">
<p role="alert" className="mt-2 text-sm text-destructive">
{t("publish.illegalError")}
</p>
)}
{errorKind === "other" && (
<p role="alert" className="mt-2 text-sm text-red-600">
<p role="alert" className="mt-2 text-sm text-destructive">
{t("form.rejected")}
</p>
)}
+5 -5
View File
@@ -71,7 +71,7 @@ export function SearchPanel() {
type="button"
aria-pressed={active}
onClick={() => setVisibility(value)}
className={`rounded px-2 py-0.5 ${active ? "bg-indigo-600 text-white" : "border"}`}
className={`rounded-md px-2 py-0.5 ${active ? "bg-primary text-primary-foreground" : "border"}`}
>
{value === "all" ? t("search.all") : t(`visibility.${value}`)}
</button>
@@ -81,7 +81,7 @@ export function SearchPanel() {
</div>
<div className="flex-1 overflow-auto">
{!hasQuery && <p className="p-4 text-sm text-neutral-400">{t("search.prompt")}</p>}
{!hasQuery && <p className="p-4 text-sm text-muted-foreground">{t("search.prompt")}</p>}
{hasQuery && search.isLoading && (
<div className="space-y-2 p-3">
@@ -92,7 +92,7 @@ export function SearchPanel() {
)}
{hasQuery && search.isError && (
<p className="p-4 text-sm text-red-600">
<p className="p-4 text-sm text-destructive">
{search.error instanceof HttpError && search.error.status === 503
? t("search.unavailable")
: t("search.loadError")}
@@ -100,12 +100,12 @@ export function SearchPanel() {
)}
{hasQuery && !search.isLoading && !search.isError && hits.length === 0 && (
<p className="p-4 text-sm text-neutral-500">{t("search.empty")}</p>
<p className="p-4 text-sm text-muted-foreground">{t("search.empty")}</p>
)}
{hits.length > 0 && (
<>
<p className="px-3 pt-2 text-xs text-neutral-500">
<p className="px-3 pt-2 text-xs text-muted-foreground">
{t("search.resultCount", { count: total })}
</p>
<ul>
+3 -3
View File
@@ -12,16 +12,16 @@ export function SearchResultRow({ hit }: { hit: SearchHitView }) {
<NavLink
to={`/search/${hit.id}`}
className={({ isActive }) =>
`block border-b px-3 py-2 ${isActive ? "bg-indigo-50" : "hover:bg-neutral-50"}`
`block border-b px-3 py-2 ${isActive ? "bg-primary/10" : "hover:bg-muted"}`
}
>
<div className="text-sm font-semibold">{hit.object_name}</div>
<div className="mt-0.5 flex items-center gap-2 text-xs text-neutral-500">
<div className="mt-0.5 flex items-center gap-2 text-xs text-muted-foreground">
<span>{hit.object_number}</span>
<VisibilityBadge visibility={hit.visibility} />
</div>
{hit.snippet && (
<p className="mt-1 line-clamp-2 text-xs text-neutral-600">
<p className="mt-1 line-clamp-2 text-xs text-muted-foreground">
<Highlight text={hit.snippet} />
</p>
)}
+1 -1
View File
@@ -4,7 +4,7 @@ export function SelectSearchPrompt() {
const { t } = useTranslation();
return (
<div className="flex h-full items-center justify-center p-4 text-sm text-neutral-400">
<div className="flex h-full items-center justify-center p-4 text-sm text-muted-foreground">
{t("search.selectPrompt")}
</div>
);
+1 -1
View File
@@ -11,7 +11,7 @@ export function LangSwitch() {
key={lng}
onClick={() => setLocale(lng)}
aria-pressed={base === lng}
className={base === lng ? "font-bold" : "text-neutral-400"}
className={base === lng ? "font-bold" : "text-muted-foreground"}
>
{lng.toUpperCase()}
</button>
+5 -5
View File
@@ -41,10 +41,10 @@ function readStored(): boolean {
function navLinkClass(collapsed: boolean) {
return ({ isActive }: { isActive: boolean }) =>
cn(
"flex items-center gap-2 rounded px-2 py-1 outline-none",
"flex items-center gap-2 rounded-md px-2 py-1 outline-none",
"focus-visible:ring-3 focus-visible:ring-ring/50",
collapsed && "justify-center",
isActive && "bg-neutral-200 font-medium",
isActive && "bg-accent font-medium",
);
}
@@ -68,7 +68,7 @@ export function Sidebar() {
return (
<aside
className={cn(
"flex shrink-0 flex-col border-r bg-neutral-50 p-3 transition-[width]",
"flex shrink-0 flex-col border-r bg-muted p-3 transition-[width]",
collapsed ? "w-14" : "w-44",
)}
>
@@ -82,8 +82,8 @@ export function Sidebar() {
aria-label={t(collapsed ? "nav.expandSidebar" : "nav.collapseSidebar")}
title={t(collapsed ? "nav.expandSidebar" : "nav.collapseSidebar")}
className={cn(
"flex items-center justify-center rounded p-1 outline-none",
"hover:bg-neutral-200 focus-visible:ring-3 focus-visible:ring-ring/50",
"flex items-center justify-center rounded-md p-1 outline-none",
"hover:bg-accent focus-visible:ring-3 focus-visible:ring-ring/50",
"disabled:pointer-events-none disabled:opacity-50",
)}
>
+1 -1
View File
@@ -4,7 +4,7 @@ export function SelectVocabularyPrompt() {
const { t } = useTranslation();
return (
<div className="flex h-full items-center justify-center p-4 text-sm text-neutral-400">
<div className="flex h-full items-center justify-center p-4 text-sm text-muted-foreground">
{t("vocab.selectPrompt")}
</div>
);
+6 -6
View File
@@ -45,20 +45,20 @@ export function VocabularyList() {
</Button>
</div>
{create.isError && (
<p role="alert" className="text-xs text-red-600">
<p role="alert" className="text-xs text-destructive">
{t("form.rejected")}
</p>
)}
</form>
<ul className="flex-1 overflow-auto">
{isLoading && (
<li className="p-3 text-sm text-neutral-400"></li>
<li className="p-3 text-sm text-muted-foreground"></li>
)}
{isError && (
<li className="p-3 text-sm text-red-600">{t("vocab.loadError")}</li>
<li className="p-3 text-sm text-destructive">{t("vocab.loadError")}</li>
)}
{data?.length === 0 && (
<li className="p-3 text-sm text-neutral-500">{t("vocab.empty")}</li>
<li className="p-3 text-sm text-muted-foreground">{t("vocab.empty")}</li>
)}
{data?.map((v) => (
<li key={v.id} className="flex items-center gap-1 border-b pr-2">
@@ -85,7 +85,7 @@ export function VocabularyList() {
{t("form.cancel")}
</Button>
{renameVocabulary.isError && (
<p role="alert" className="text-xs text-red-600">
<p role="alert" className="text-xs text-destructive">
{t("form.rejected")}
</p>
)}
@@ -95,7 +95,7 @@ export function VocabularyList() {
<NavLink
to={`/vocabularies/${v.id}`}
className={({ isActive }) =>
`block flex-1 px-3 py-2 text-sm ${isActive ? "bg-indigo-50" : "hover:bg-neutral-50"}`
`block flex-1 px-3 py-2 text-sm ${isActive ? "bg-primary/10" : "hover:bg-muted"}`
}
>
{v.key}
+6 -6
View File
@@ -49,18 +49,18 @@ export function VocabularyTerms() {
return (
<div className="overflow-auto p-4">
<h3 className="mb-2 text-sm font-medium uppercase text-neutral-500">
<h3 className="mb-2 label-caption">
{t("vocab.terms")}
</h3>
<ul className="mb-4">
{isLoading && (
<li className="text-sm text-neutral-400"></li>
<li className="text-sm text-muted-foreground"></li>
)}
{isError && (
<li className="text-sm text-red-600">{t("vocab.loadError")}</li>
<li className="text-sm text-destructive">{t("vocab.loadError")}</li>
)}
{!isLoading && !isError && terms?.length === 0 && (
<li className="text-sm text-neutral-500">{t("vocab.noTerms")}</li>
<li className="text-sm text-muted-foreground">{t("vocab.noTerms")}</li>
)}
{terms?.map((term) => (
<TermRow key={term.id} vocabularyId={id} term={term} lang={lang} />
@@ -78,12 +78,12 @@ export function VocabularyTerms() {
/>
</div>
{error && (
<p role="alert" className="text-xs text-red-600">
<p role="alert" className="text-xs text-destructive">
{t("form.required")}
</p>
)}
{addTerm.isError && (
<p role="alert" className="text-xs text-red-600">
<p role="alert" className="text-xs text-destructive">
{t("form.rejected")}
</p>
)}