fix(web): keep secondary shell text readable in the light theme
Secondary shell text was muted text dimmed further with opacity: the
section labels, the projects caption, the sidebar "+" icons, the privacy
panel, the trading subtitle and the Heute brief's Mission Control link.
Muted text measures 4.4:1 on the light sidebar, so these fell to 1.9 to
3.4:1 in the light theme; a few were below 4.5:1 in dark as well.
- Secondary sidebar text and icons share `rk-sidebar-muted`, the sidebar
foreground at 65% that row times already used (5.0:1 on the light
sidebar). Row times now use the class too.
- Section labels and space or project group titles use the dimmed
foreground in light and keep the 80% muted color in dark; group titles
move up from 60%, which was 3.4:1 in dark.
- The privacy panel heading is plain text with a success-colored check
instead of emerald text, and its details pass 4.5:1 in both themes.
- Needs-you chips keep the warning tint and border; their label uses the
foreground color in light.
- The Mission Control link uses the full muted color.
- E2E: the shell test checks every text in the sidebar except the logo,
and the Heute brief, in both themes, and captures
`shell-contrast-{light,dark}-heute`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SSVQGR4WU92LaddTP19GcQ
This commit is contained in:
parent
dc1d28c670
commit
53ec88ac7d
@ -16,11 +16,12 @@ test.use({ viewport: { width: 1280, height: 1000 } });
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Texts in `locator` below WCAG AA against the surface they are painted on. With `inside`, every
|
* Texts in `locator` below WCAG AA against the surface they are painted on. With `inside`, every
|
||||||
* visible text and typed value within it counts, except in disabled controls, which WCAG exempts.
|
* visible text and typed value within it counts, except in disabled controls, which WCAG exempts,
|
||||||
|
* and in elements matching `skip`.
|
||||||
*/
|
*/
|
||||||
function unreadableTexts(locator: Locator, inside: boolean) {
|
function unreadableTexts(locator: Locator, inside: boolean, skip: string | null) {
|
||||||
return locator.evaluate(
|
return locator.evaluate(
|
||||||
(root, { inside, min }) => {
|
(root, { inside, skip, min }) => {
|
||||||
const context = document
|
const context = document
|
||||||
.createElement("canvas")
|
.createElement("canvas")
|
||||||
.getContext("2d", { willReadFrequently: true });
|
.getContext("2d", { willReadFrequently: true });
|
||||||
@ -81,7 +82,11 @@ function unreadableTexts(locator: Locator, inside: boolean) {
|
|||||||
};
|
};
|
||||||
const elements = inside
|
const elements = inside
|
||||||
? [root, ...root.querySelectorAll("*")].filter(
|
? [root, ...root.querySelectorAll("*")].filter(
|
||||||
(element) => ownText(element) && shown(element) && !element.closest(":disabled"),
|
(element) =>
|
||||||
|
ownText(element) &&
|
||||||
|
shown(element) &&
|
||||||
|
!element.closest(":disabled") &&
|
||||||
|
!(skip && element.closest(skip)),
|
||||||
)
|
)
|
||||||
: [root];
|
: [root];
|
||||||
return elements.flatMap((element) => {
|
return elements.flatMap((element) => {
|
||||||
@ -90,14 +95,18 @@ function unreadableTexts(locator: Locator, inside: boolean) {
|
|||||||
return ratio < min ? [`${ratio.toFixed(2)}:1 ${text.slice(0, 48)}`] : [];
|
return ratio < min ? [`${ratio.toFixed(2)}:1 ${text.slice(0, 48)}`] : [];
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{ inside, min: MIN_CONTRAST },
|
{ inside, skip, min: MIN_CONTRAST },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function expectReadable(locator: Locator, what: string, { inside = false } = {}) {
|
async function expectReadable(
|
||||||
|
locator: Locator,
|
||||||
|
what: string,
|
||||||
|
{ inside = false, skip = null }: { inside?: boolean; skip?: string | null } = {},
|
||||||
|
) {
|
||||||
await expect(locator).toBeVisible();
|
await expect(locator).toBeVisible();
|
||||||
await expect
|
await expect
|
||||||
.poll(() => unreadableTexts(locator, inside), { message: `${what} is readable` })
|
.poll(() => unreadableTexts(locator, inside, skip), { message: `${what} is readable` })
|
||||||
.toEqual([]);
|
.toEqual([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,6 +222,9 @@ test("shell surfaces stay readable in light and dark themes", async ({ page }, t
|
|||||||
await composer.fill("Draft a note about owls");
|
await composer.fill("Draft a note about owls");
|
||||||
const sidebar = page.getByTestId("bots-sidebar");
|
const sidebar = page.getByTestId("bots-sidebar");
|
||||||
const sidePanel = page.getByTestId("side-panel");
|
const sidePanel = page.getByTestId("side-panel");
|
||||||
|
await expect(sidebar.locator("[data-main-conversation]")).toBeVisible();
|
||||||
|
await expect(sidebar.getByRole("button", { name: "Researcher, 1:1 conversation" })).toBeVisible();
|
||||||
|
await expect(page.getByTestId("trading-mode-status")).toBeVisible();
|
||||||
|
|
||||||
await inEachTheme(page, async (theme) => {
|
await inEachTheme(page, async (theme) => {
|
||||||
await captureScreenshot(page, testInfo, `shell-contrast-${theme}`);
|
await captureScreenshot(page, testInfo, `shell-contrast-${theme}`);
|
||||||
@ -220,27 +232,9 @@ test("shell surfaces stay readable in light and dark themes", async ({ page }, t
|
|||||||
await expectReadable(page.getByTestId("composer-bar"), `${theme}: the composer`, {
|
await expectReadable(page.getByTestId("composer-bar"), `${theme}: the composer`, {
|
||||||
inside: true,
|
inside: true,
|
||||||
});
|
});
|
||||||
await expectReadable(
|
// The header carries the logo, which WCAG exempts; its create button is checked on its own.
|
||||||
sidebar.getByRole("button", { name: "Heute", exact: true }),
|
await expectReadable(sidebar, `${theme}: the sidebar`, { inside: true, skip: ".app-drag" });
|
||||||
`${theme}: Heute`,
|
await expectReadable(page.getByTestId("create-menu-trigger"), `${theme}: the create button`);
|
||||||
{ inside: true },
|
|
||||||
);
|
|
||||||
await expectReadable(
|
|
||||||
sidebar.locator("[data-main-conversation]"),
|
|
||||||
`${theme}: the main conversation row`,
|
|
||||||
{ inside: true },
|
|
||||||
);
|
|
||||||
await expectReadable(
|
|
||||||
sidebar.getByRole("button", { name: "Researcher, 1:1 conversation" }),
|
|
||||||
`${theme}: the selected bot row`,
|
|
||||||
{ inside: true },
|
|
||||||
);
|
|
||||||
await expectReadable(page.getByTestId("trading-mode-status"), `${theme}: trading status`, {
|
|
||||||
inside: true,
|
|
||||||
});
|
|
||||||
await expectReadable(page.getByTestId("user-menu-trigger"), `${theme}: the user menu`, {
|
|
||||||
inside: true,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await openNewBot(page);
|
await openNewBot(page);
|
||||||
@ -259,4 +253,12 @@ test("shell surfaces stay readable in light and dark themes", async ({ page }, t
|
|||||||
await captureScreenshot(page, testInfo, `shell-contrast-${theme}-settings`);
|
await captureScreenshot(page, testInfo, `shell-contrast-${theme}-settings`);
|
||||||
await expectReadable(sidePanel, `${theme}: the bot settings form`, { inside: true });
|
await expectReadable(sidePanel, `${theme}: the bot settings form`, { inside: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await sidebar.getByRole("button", { name: "Heute", exact: true }).click();
|
||||||
|
const heute = page.getByTestId("heute-brief");
|
||||||
|
await expect(heute.getByTestId("heute-mission-control-link")).toBeVisible();
|
||||||
|
await inEachTheme(page, async (theme) => {
|
||||||
|
await captureScreenshot(page, testInfo, `shell-contrast-${theme}-heute`);
|
||||||
|
await expectReadable(heute, `${theme}: the Heute brief`, { inside: true });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -10,16 +10,16 @@ const TRUST_ITEMS = [
|
|||||||
export const DataTrustPanel = memo(function DataTrustPanel() {
|
export const DataTrustPanel = memo(function DataTrustPanel() {
|
||||||
return (
|
return (
|
||||||
<div className="hidden xl:flex flex-col items-start gap-1.5 rounded-xl border border-border/40 bg-muted/30 px-3 py-2.5">
|
<div className="hidden xl:flex flex-col items-start gap-1.5 rounded-xl border border-border/40 bg-muted/30 px-3 py-2.5">
|
||||||
<div className="flex items-center gap-1.5 text-xs font-medium text-emerald-600 dark:text-emerald-400">
|
<div className="flex items-center gap-1.5 text-xs font-medium text-sidebar-foreground">
|
||||||
<CheckCircle2 className="size-3.5" />
|
<CheckCircle2 className="size-3.5 text-success" />
|
||||||
<span>Datenschutz aktiv</span>
|
<span>Datenschutz aktiv</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
{TRUST_ITEMS.map(({ icon: Icon, label, desc }) => (
|
{TRUST_ITEMS.map(({ icon: Icon, label, desc }) => (
|
||||||
<div key={label} className="flex items-center gap-1.5">
|
<div key={label} className="flex items-center gap-1.5 rk-sidebar-muted">
|
||||||
<Icon className="size-3 text-muted-foreground/60" />
|
<Icon className="size-3" />
|
||||||
<span className="text-[11px] leading-tight text-muted-foreground">{label}</span>
|
<span className="text-[11px] leading-tight">{label}</span>
|
||||||
<span className="text-[10px] leading-tight text-muted-foreground/50">· {desc}</span>
|
<span className="text-[10px] leading-tight">· {desc}</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -45,7 +45,7 @@ export function TradingModeIndicator({
|
|||||||
<TradingModeLabel label={presentation.label} />
|
<TradingModeLabel label={presentation.label} />
|
||||||
</span>
|
</span>
|
||||||
{presentation.subtitle ? (
|
{presentation.subtitle ? (
|
||||||
<span className="block truncate text-ui-body-sm text-muted-foreground/70">
|
<span className="block truncate text-ui-body-sm rk-sidebar-muted">
|
||||||
<TradingModeSubtitleText subtitle={presentation.subtitle} />
|
<TradingModeSubtitleText subtitle={presentation.subtitle} />
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@ -178,7 +178,7 @@ export function HeuteBrief({
|
|||||||
href={MISSION_CONTROL_URL}
|
href={MISSION_CONTROL_URL}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
className="inline-flex items-center gap-1.5 rounded-lg px-2 py-1 text-ui-label text-muted-foreground/70 transition-colors hover:text-muted-foreground"
|
className="inline-flex items-center gap-1.5 rounded-lg px-2 py-1 text-ui-label text-muted-foreground transition-colors hover:text-foreground"
|
||||||
data-testid="heute-mission-control-link"
|
data-testid="heute-mission-control-link"
|
||||||
>
|
>
|
||||||
<Trans>Mission Control</Trans>
|
<Trans>Mission Control</Trans>
|
||||||
@ -235,7 +235,7 @@ function EmptyDay({ onStartChat, hasBots }: { onStartChat: () => void; hasBots:
|
|||||||
|
|
||||||
function ActionableChip({ label }: { label: string }) {
|
function ActionableChip({ label }: { label: string }) {
|
||||||
return (
|
return (
|
||||||
<span className="shrink-0 rounded-full border border-warning/30 bg-warning/10 px-2 py-0.5 text-ui-label font-medium text-warning">
|
<span className="shrink-0 rounded-full border border-warning/30 bg-warning/10 px-2 py-0.5 text-ui-label font-medium text-foreground dark:text-warning">
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -3456,7 +3456,7 @@ export function ShellPage() {
|
|||||||
className={`app-no-drag flex size-control-sm items-center justify-center rounded-full ${
|
className={`app-no-drag flex size-control-sm items-center justify-center rounded-full ${
|
||||||
activityMode
|
activityMode
|
||||||
? "bg-primary text-primary-foreground"
|
? "bg-primary text-primary-foreground"
|
||||||
: "text-muted-foreground/70 hover:text-foreground/75"
|
: "rk-sidebar-muted hover:text-foreground/75"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Bell
|
<Bell
|
||||||
@ -3469,7 +3469,7 @@ export function ShellPage() {
|
|||||||
|
|
||||||
<Popover open={createMenuOpen} onOpenChange={setCreateMenuOpen}>
|
<Popover open={createMenuOpen} onOpenChange={setCreateMenuOpen}>
|
||||||
<PopoverTrigger
|
<PopoverTrigger
|
||||||
className="app-no-drag text-[21px] text-muted-foreground/70 hover:text-foreground/75"
|
className="app-no-drag text-[21px] rk-sidebar-muted hover:text-foreground/75"
|
||||||
title={t`Create`}
|
title={t`Create`}
|
||||||
data-testid="create-menu-trigger"
|
data-testid="create-menu-trigger"
|
||||||
>
|
>
|
||||||
@ -3592,7 +3592,7 @@ export function ShellPage() {
|
|||||||
type="button"
|
type="button"
|
||||||
aria-label={t`Neue Unterhaltung`}
|
aria-label={t`Neue Unterhaltung`}
|
||||||
data-testid="conversation-picker-trigger"
|
data-testid="conversation-picker-trigger"
|
||||||
className="text-muted-foreground/60 hover:text-foreground/90 transition-colors"
|
className="rk-sidebar-muted hover:text-foreground/90 transition-colors"
|
||||||
>
|
>
|
||||||
<Plus strokeWidth={2.5} className="size-icon-md" />
|
<Plus strokeWidth={2.5} className="size-icon-md" />
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
@ -3633,7 +3633,7 @@ export function ShellPage() {
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{conversationSidebarGroups.length === 0 && !mainConversationEntry ? (
|
{conversationSidebarGroups.length === 0 && !mainConversationEntry ? (
|
||||||
<div className="p-4 text-center text-ui-label text-muted-foreground">
|
<div className="p-4 text-center text-ui-label rk-sidebar-muted">
|
||||||
<p>{t`Noch keine Unterhaltung.`}</p>
|
<p>{t`Noch keine Unterhaltung.`}</p>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@ -3657,7 +3657,7 @@ export function ShellPage() {
|
|||||||
<div className="flex items-center pt-3 pb-0.5">
|
<div className="flex items-center pt-3 pb-0.5">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="flex min-w-0 flex-1 items-center justify-between gap-2 rounded-lg px-2.5 py-1 text-ui-label font-semibold tracking-wider uppercase text-muted-foreground/60 hover:text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring"
|
className="flex min-w-0 flex-1 items-center justify-between gap-2 rounded-lg px-2.5 py-1 text-ui-label font-semibold tracking-wider uppercase text-foreground/65 dark:text-muted-foreground/80 hover:text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (group.emptySpaceId) {
|
if (group.emptySpaceId) {
|
||||||
openSpaceChat(group.emptySpaceId, "/onboarding");
|
openSpaceChat(group.emptySpaceId, "/onboarding");
|
||||||
@ -3841,7 +3841,7 @@ export function ShellPage() {
|
|||||||
{item.chat.name}
|
{item.chat.name}
|
||||||
</span>
|
</span>
|
||||||
{needsYouChipLabel(item.chat.status) ? (
|
{needsYouChipLabel(item.chat.status) ? (
|
||||||
<span className="shrink-0 rounded-full border border-warning/30 bg-warning/10 px-2 py-0.5 text-ui-label font-medium text-warning">
|
<span className="shrink-0 rounded-full border border-warning/30 bg-warning/10 px-2 py-0.5 text-ui-label font-medium text-foreground dark:text-warning">
|
||||||
{needsYouChipLabel(item.chat.status)}
|
{needsYouChipLabel(item.chat.status)}
|
||||||
</span>
|
</span>
|
||||||
) : item.chat.title ? (
|
) : item.chat.title ? (
|
||||||
@ -3887,7 +3887,7 @@ export function ShellPage() {
|
|||||||
[group.key]: !prev[group.key],
|
[group.key]: !prev[group.key],
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
className="mt-1 w-full flex items-center justify-center gap-1.5 rounded-lg px-2 py-1.5 text-ui-label font-medium text-muted-foreground hover:bg-sidebar-accent hover:text-foreground transition-colors"
|
className="mt-1 w-full flex items-center justify-center gap-1.5 rounded-lg px-2 py-1.5 text-ui-label font-medium rk-sidebar-muted hover:bg-sidebar-accent hover:text-foreground transition-colors"
|
||||||
>
|
>
|
||||||
{expandedTeamGroups[group.key]
|
{expandedTeamGroups[group.key]
|
||||||
? "Weniger anzeigen"
|
? "Weniger anzeigen"
|
||||||
@ -3902,7 +3902,7 @@ export function ShellPage() {
|
|||||||
<div className="flex items-center justify-between pt-4 pb-1 mt-3 border-t border-sidebar-border px-2.5">
|
<div className="flex items-center justify-between pt-4 pb-1 mt-3 border-t border-sidebar-border px-2.5">
|
||||||
<div>
|
<div>
|
||||||
<div className="rk-shell-section-label">{t`Projekte`}</div>
|
<div className="rk-shell-section-label">{t`Projekte`}</div>
|
||||||
<p className="mt-0.5 px-0.5 text-ui-caption text-muted-foreground/70">
|
<p className="mt-0.5 px-0.5 text-ui-caption rk-sidebar-muted">
|
||||||
{t`Threads by topic · folders`}
|
{t`Threads by topic · folders`}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -3912,14 +3912,14 @@ export function ShellPage() {
|
|||||||
aria-label={t`Create a project`}
|
aria-label={t`Create a project`}
|
||||||
data-testid="create-project-trigger"
|
data-testid="create-project-trigger"
|
||||||
onClick={() => openNewSpaceDialog("project")}
|
onClick={() => openNewSpaceDialog("project")}
|
||||||
className="text-muted-foreground/60 hover:text-foreground/90 transition-colors"
|
className="rk-sidebar-muted hover:text-foreground/90 transition-colors"
|
||||||
>
|
>
|
||||||
<Plus strokeWidth={2.5} className="size-icon-md" />
|
<Plus strokeWidth={2.5} className="size-icon-md" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{projectSidebarGroups.length === 0 ? (
|
{projectSidebarGroups.length === 0 ? (
|
||||||
<div className="p-4 text-center text-ui-label text-muted-foreground">
|
<div className="p-4 text-center text-ui-label rk-sidebar-muted">
|
||||||
<p>{t`No projects yet.`}</p>
|
<p>{t`No projects yet.`}</p>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@ -3957,7 +3957,7 @@ export function ShellPage() {
|
|||||||
</ProjectDragGrip>
|
</ProjectDragGrip>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="flex min-w-0 flex-1 items-center justify-between gap-2 rounded-lg pl-8 py-1 text-ui-label font-semibold tracking-wider uppercase text-muted-foreground/60 hover:text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring"
|
className="flex min-w-0 flex-1 items-center justify-between gap-2 rounded-lg pl-8 py-1 text-ui-label font-semibold tracking-wider uppercase text-foreground/65 dark:text-muted-foreground/80 hover:text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (group.emptySpaceId) {
|
if (group.emptySpaceId) {
|
||||||
openSpaceChat(group.emptySpaceId, "/app");
|
openSpaceChat(group.emptySpaceId, "/app");
|
||||||
@ -4153,7 +4153,7 @@ export function ShellPage() {
|
|||||||
type="button"
|
type="button"
|
||||||
aria-expanded={archivedOpen}
|
aria-expanded={archivedOpen}
|
||||||
onClick={() => setArchivedOpen((open) => !open)}
|
onClick={() => setArchivedOpen((open) => !open)}
|
||||||
className="flex w-full items-center justify-between rounded-lg px-2.5 py-2 text-ui-body text-muted-foreground hover:bg-sidebar-accent"
|
className="flex w-full items-center justify-between rounded-lg px-2.5 py-2 text-ui-body rk-sidebar-muted hover:bg-sidebar-accent"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
<Trans>Archived</Trans>
|
<Trans>Archived</Trans>
|
||||||
|
|||||||
@ -11,9 +11,7 @@ export function rosterNameClass(unread: boolean): string {
|
|||||||
return unread ? "font-bold text-sidebar-foreground" : "font-semibold text-sidebar-foreground";
|
return unread ? "font-bold text-sidebar-foreground" : "font-semibold text-sidebar-foreground";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Muted text measures about 4.4:1 on the light sidebar; a dimmed foreground stays above 4.5:1.
|
export const rosterTimeClass = "text-ui-body-sm rk-sidebar-muted tabular-nums font-medium";
|
||||||
export const rosterTimeClass =
|
|
||||||
"text-ui-body-sm text-sidebar-foreground/65 tabular-nums font-medium";
|
|
||||||
|
|
||||||
export const rosterUnreadDotClass = "inline-block h-2 w-2 rounded-full bg-sidebar-foreground";
|
export const rosterUnreadDotClass = "inline-block h-2 w-2 rounded-full bg-sidebar-foreground";
|
||||||
|
|
||||||
|
|||||||
@ -73,7 +73,7 @@ body,
|
|||||||
/* Shell layout utilities — token-backed, use instead of one-off pixel classes */
|
/* Shell layout utilities — token-backed, use instead of one-off pixel classes */
|
||||||
@layer components {
|
@layer components {
|
||||||
.rk-shell-section-label {
|
.rk-shell-section-label {
|
||||||
@apply text-ui-label font-bold tracking-wider uppercase text-muted-foreground/80;
|
@apply text-ui-label font-bold tracking-wider uppercase text-foreground/65 dark:text-muted-foreground/80;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rk-shell-icon-btn {
|
.rk-shell-icon-btn {
|
||||||
@ -84,6 +84,12 @@ body,
|
|||||||
@apply text-ui-body-sm text-muted-foreground;
|
@apply text-ui-body-sm text-muted-foreground;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Secondary sidebar text and icons. Muted text measures 4.4:1 on the light sidebar;
|
||||||
|
a dimmed foreground stays above 4.5:1. */
|
||||||
|
.rk-sidebar-muted {
|
||||||
|
@apply text-sidebar-foreground/65;
|
||||||
|
}
|
||||||
|
|
||||||
.rk-shell-row-title {
|
.rk-shell-row-title {
|
||||||
@apply text-ui-body font-medium text-foreground;
|
@apply text-ui-body font-medium text-foreground;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user