Merge branch 'development' into player-picker-single-sele

# Conflicts:
#	src/locales/de/messages.po
#	src/locales/en/messages.po
#	src/locales/es/messages.po
#	src/locales/ja/messages.po
This commit is contained in:
2026-08-23 17:59:55 -07:00
81 changed files with 13127 additions and 286 deletions
+5 -1
View File
@@ -1,7 +1,11 @@
{ {
"name": "tanstack-start-example-basic-react-query", "name": "tanstack-start-example-basic-react-query",
"private": true, "private": true,
"sideEffects": false, "sideEffects": [
"**/*.css",
"**/themes/styles.ts",
"**/themes/theme-styles.ts"
],
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite dev --host 0.0.0.0", "dev": "vite dev --host 0.0.0.0",
+11 -1
View File
@@ -203,7 +203,17 @@ function RootDocument({ children }: { children: React.ReactNode }) {
media="(prefers-color-scheme: dark)" media="(prefers-color-scheme: dark)"
content={CHROME_COLORS.dark.base} content={CHROME_COLORS.dark.base}
/> />
<ColorSchemeScript /> <ColorSchemeScript defaultColorScheme="auto" />
{/* Pre-hydration theme restore from ThemeProvider's localStorage
mirrors: data-theme (so exotic themes don't flash default),
forced color scheme (dark-only themes must not paint light),
and the theme's font stylesheets (avoid FOUT until hydration). */}
<script
dangerouslySetInnerHTML={{
__html:
'try{var d=document.documentElement,t=localStorage.getItem("flxn-theme");if(t&&t!=="default"){d.setAttribute("data-theme",t);}var s=localStorage.getItem("flxn-theme-scheme");if(s){d.setAttribute("data-mantine-color-scheme",s);}var f=JSON.parse(localStorage.getItem("flxn-theme-fonts")||"[]");for(var i=0;i<f.length;i++){var l=document.createElement("link");l.rel="stylesheet";l.href=f[i];l.setAttribute("data-flxn-theme-font",t||"default");document.head.appendChild(l);}}catch(e){}',
}}
/>
<link rel="stylesheet" href="/styles.css" /> <link rel="stylesheet" href="/styles.css" />
</head> </head>
<body <body
+2
View File
@@ -2,6 +2,7 @@ import { createFileRoute } from "@tanstack/react-router";
import { Box, Title, Stack } from "@mantine/core"; import { Box, Title, Stack } from "@mantine/core";
import { ColorSchemePicker } from "@/features/settings/components/color-scheme-picker"; import { ColorSchemePicker } from "@/features/settings/components/color-scheme-picker";
import AccentColorPicker from "@/features/settings/components/accent-color-picker"; import AccentColorPicker from "@/features/settings/components/accent-color-picker";
import ThemePicker from "@/features/settings/components/theme-picker";
import LocalePicker from "@/features/settings/components/locale-picker"; import LocalePicker from "@/features/settings/components/locale-picker";
import { NotificationsSection } from "@/features/settings/components/notifications-section"; import { NotificationsSection } from "@/features/settings/components/notifications-section";
import { SignOutIcon } from "@phosphor-icons/react"; import { SignOutIcon } from "@phosphor-icons/react";
@@ -33,6 +34,7 @@ function RouteComponent() {
> >
<Title order={3}><Trans>Appearance</Trans></Title> <Title order={3}><Trans>Appearance</Trans></Title>
<Stack> <Stack>
<ThemePicker />
<AccentColorPicker /> <AccentColorPicker />
<ColorSchemePicker /> <ColorSchemePicker />
<LocalePicker /> <LocalePicker />
+28 -8
View File
@@ -1,7 +1,7 @@
import { import {
Avatar as MantineAvatar, Avatar as MantineAvatar,
AvatarProps as MantineAvatarProps, AvatarProps as MantineAvatarProps,
Paper, Box,
Modal, Modal,
Image, Image,
Group, Group,
@@ -22,10 +22,17 @@ interface AvatarProps
contain?: boolean; contain?: boolean;
} }
// Mantine radius tokens ("sm", "xl") are valid as props but not as raw CSS
// values — resolve them before using in style/borderRadius.
const resolveRadius = (radius: string | number) =>
typeof radius === "string" && /^[a-z]+$/.test(radius)
? `var(--mantine-radius-${radius})`
: radius;
const Avatar = ({ const Avatar = ({
name, name,
size = 35, size = 35,
radius = "100%", radius = "var(--flxn-avatar-radius, 50%)",
withBorder = true, withBorder = true,
disableFullscreen = false, disableFullscreen = false,
contain = false, contain = false,
@@ -34,6 +41,7 @@ const Avatar = ({
const { t } = useLingui(); const { t } = useLingui();
const [isFullscreenOpen, setIsFullscreenOpen] = useState(false); const [isFullscreenOpen, setIsFullscreenOpen] = useState(false);
const hasImage = Boolean(props.src); const hasImage = Boolean(props.src);
const frameRadius = resolveRadius(radius);
const handleAvatarClick = () => { const handleAvatarClick = () => {
if (hasImage && !disableFullscreen) { if (hasImage && !disableFullscreen) {
@@ -43,11 +51,20 @@ const Avatar = ({
return ( return (
<> <>
<Paper <Box
p={size / 20} className="flxn-avatar-frame"
radius={radius} p={withBorder ? size / 20 : 0}
withBorder={withBorder}
style={{ style={{
// Plain Box, not Paper: the frame must own the avatar's single
// border (via --flxn-avatar-border) and stay immune to generic
// theme Paper restyles; shape comes from --flxn-avatar-radius.
borderRadius: frameRadius,
border: withBorder
? 'var(--flxn-avatar-border, 1px solid var(--mantine-color-default-border))'
: undefined,
backgroundColor: withBorder
? 'var(--mantine-color-body)'
: undefined,
cursor: hasImage && !disableFullscreen ? 'pointer' : 'default', cursor: hasImage && !disableFullscreen ? 'pointer' : 'default',
transition: 'transform 0.15s ease', transition: 'transform 0.15s ease',
}} }}
@@ -69,14 +86,17 @@ const Avatar = ({
size={size} size={size}
radius={radius} radius={radius}
w={size} w={size}
{...props}
styles={{ styles={{
root: {
borderRadius: frameRadius,
},
image: { image: {
objectFit: contain ? 'contain' : 'cover', objectFit: contain ? 'contain' : 'cover',
}, },
}} }}
{...props}
/> />
</Paper> </Box>
<Modal <Modal
opened={isFullscreenOpen} opened={isFullscreenOpen}
+13 -5
View File
@@ -1,7 +1,13 @@
import { Box, Container, Title, useComputedColorScheme } from "@mantine/core"; import { Box, Container, Title, useComputedColorScheme } from "@mantine/core";
import { PropsWithChildren, useEffect, useRef } from "react"; import { PropsWithChildren, useEffect, useRef } from "react";
import { Drawer as VaulDrawer } from "vaul"; import { Drawer as VaulDrawer } from "vaul";
import { CHROME_COLORS, setThemeColorMeta } from "@/lib/mantine/theme-colors"; import {
pushDimmedThemeColor,
popDimmedThemeColor,
} from "@/lib/mantine/theme-colors";
import { getTheme } from "@/themes/registry";
import { resolveChromeColors } from "@/themes/chrome";
import { useAuth } from "@/contexts/auth-context";
import styles from "./styles.module.css"; import styles from "./styles.module.css";
interface DrawerProps extends PropsWithChildren { interface DrawerProps extends PropsWithChildren {
@@ -20,22 +26,24 @@ const Drawer: React.FC<DrawerProps> = ({
onExited, onExited,
}) => { }) => {
const colorScheme = useComputedColorScheme("light"); const colorScheme = useComputedColorScheme("light");
const { metadata } = useAuth();
const contentRef = useRef<HTMLDivElement>(null); const contentRef = useRef<HTMLDivElement>(null);
const themeId = metadata.theme;
useEffect(() => { useEffect(() => {
if (!opened) return; if (!opened) return;
const appElement = document.querySelector<HTMLElement>(".app"); const appElement = document.querySelector<HTMLElement>(".app");
const colors = CHROME_COLORS[colorScheme]; const colors = resolveChromeColors(getTheme(themeId))[colorScheme];
appElement?.classList.add("drawer-scaling"); appElement?.classList.add("drawer-scaling");
setThemeColorMeta(colors.dimmed); pushDimmedThemeColor(colors.dimmed);
return () => { return () => {
appElement?.classList.remove("drawer-scaling"); appElement?.classList.remove("drawer-scaling");
setThemeColorMeta(colors.base); popDimmedThemeColor(colors.base);
}; };
}, [opened, colorScheme]); }, [opened, colorScheme, themeId]);
useEffect(() => { useEffect(() => {
if (!opened || !contentRef.current) return; if (!opened || !contentRef.current) return;
+1 -1
View File
@@ -1,7 +1,7 @@
.drawerOverlay { .drawerOverlay {
position: fixed; position: fixed;
inset: 0; inset: 0;
background-color: rgba(0, 0, 0, 0.4); background-color: var(--flxn-overlay);
z-index: 101; z-index: 101;
} }
+25 -23
View File
@@ -48,6 +48,8 @@ function SwipeableTabs({
}, [search?.tab, tabs, defaultTab]); }, [search?.tab, tabs, defaultTab]);
const [activeTab, setActiveTab] = useState(getActiveTabFromUrl); const [activeTab, setActiveTab] = useState(getActiveTabFromUrl);
const [stuck, setStuck] = useState(false);
const sentinelRef = useRef<HTMLDivElement>(null);
const [rootRef, setRootRef] = useState<HTMLDivElement | null>(null); const [rootRef, setRootRef] = useState<HTMLDivElement | null>(null);
const controlsRefs = useRef<Record<number, HTMLSpanElement | null>>({}); const controlsRefs = useRef<Record<number, HTMLSpanElement | null>>({});
const slideRefs = useRef<Record<number, HTMLDivElement | null>>({}); const slideRefs = useRef<Record<number, HTMLDivElement | null>>({});
@@ -132,6 +134,20 @@ function SwipeableTabs({
}; };
}, [activeTab, updateHeight]); }, [activeTab, updateHeight]);
// A 1px sentinel above the sticky strip: once it scrolls out of view the
// strip is pinned — flip data-stuck so themes can swap flat → docked chrome.
useEffect(() => {
const sentinel = sentinelRef.current;
if (!sentinel) return;
const observer = new IntersectionObserver(
([entry]) => setStuck(!entry.isIntersecting),
{ threshold: 0 }
);
observer.observe(sentinel);
return () => observer.disconnect();
}, []);
const setControlRef = useCallback( const setControlRef = useCallback(
(index: number) => (node: HTMLSpanElement | null) => { (index: number) => (node: HTMLSpanElement | null) => {
controlsRefs.current[index] = node; controlsRefs.current[index] = node;
@@ -148,8 +164,11 @@ function SwipeableTabs({
return ( return (
<Box style={{ touchAction: "pan-y" }}> <Box style={{ touchAction: "pan-y" }}>
<div ref={sentinelRef} aria-hidden style={{ height: 1, marginTop: -1 }} />
<Box <Box
ref={setRootRef} ref={setRootRef}
className="flxn-tabs"
data-stuck={stuck || undefined}
pos="sticky" pos="sticky"
top={0} top={0}
style={{ style={{
@@ -157,45 +176,28 @@ function SwipeableTabs({
paddingInline: "var(--mantine-spacing-md)", paddingInline: "var(--mantine-spacing-md)",
marginBottom: mb !== undefined ? mb : "var(--mantine-spacing-md)", marginBottom: mb !== undefined ? mb : "var(--mantine-spacing-md)",
zIndex: 100, zIndex: 100,
backgroundColor: "var(--mantine-color-body)",
}} }}
> >
<FloatingIndicator <FloatingIndicator
target={controlsRefs.current[activeTab]} target={controlsRefs.current[activeTab]}
parent={rootRef} parent={rootRef}
styles={{ className="flxn-tabs-indicator"
root: {
borderBottom: "2px solid var(--mantine-primary-color-filled)",
paddingInline: "0.5rem",
},
}}
/> />
{tabs.map((tab, index) => ( {tabs.map((tab, index) => (
<UnstyledButton <UnstyledButton
key={`${tab.label}-${index}`} key={`${tab.label}-${index}`}
onClick={() => changeTab(index)} onClick={() => changeTab(index)}
style={{ className="flxn-tab"
flex: 1, data-active={activeTab === index || undefined}
padding: "var(--mantine-spacing-sm) var(--mantine-spacing-xs)",
textAlign: "center",
color:
activeTab === index
? "var(--mantine-primary-color-filled)"
: "var(--mantine-color-text)",
fontWeight: activeTab === index ? 600 : 400,
transition: "color 200ms ease, font-weight 200ms ease",
backgroundColor: "transparent",
border: "none",
borderRadius: 0,
}}
> >
<Text <Text
size="sm" size="sm"
component="span" component="span"
style={{ style={{
// the FloatingIndicator sizes to this span — keep padding symmetric
display: "inline-block", display: "inline-block",
paddingInline: "0.5rem", paddingInline: "0.875rem",
paddingBottom: "0.25rem", paddingBlock: "0.375rem",
}} }}
ref={setControlRef(index)} ref={setControlRef(index)}
> >
+3 -3
View File
@@ -62,7 +62,7 @@ const TeamAvatar = ({
top: -crownSize * 1.1, top: -crownSize * 1.1,
left: "45%", left: "45%",
transform: "translateX(-50%)", transform: "translateX(-50%)",
color: "gold", color: "var(--flxn-gold)",
rotate: "-5deg", rotate: "-5deg",
}} }}
> >
@@ -83,7 +83,7 @@ const TeamAvatar = ({
top: -crownSize * 0.95, top: -crownSize * 0.95,
left: "65%", left: "65%",
transform: "translateX(-50%)", transform: "translateX(-50%)",
color: "gold", color: "var(--flxn-gold)",
rotate: "10deg", rotate: "10deg",
}} }}
> >
@@ -116,7 +116,7 @@ const TeamAvatar = ({
top: -crownSize * 0.6, top: -crownSize * 0.6,
left: -crownSize * 0.25, left: -crownSize * 0.25,
transform: "rotate(-25deg)", transform: "rotate(-25deg)",
color: "gold", color: "var(--flxn-gold)",
}} }}
> >
<CrownIcon size={crownSize} weight="fill" /> <CrownIcon size={crownSize} weight="fill" />
+1
View File
@@ -15,6 +15,7 @@ interface AuthData {
metadata: { metadata: {
accentColor: MantineColor; accentColor: MantineColor;
colorScheme: MantineColorScheme; colorScheme: MantineColorScheme;
theme?: string;
locale?: string; locale?: string;
}; };
roles: string[]; roles: string[];
@@ -208,8 +208,8 @@ const BadgeShowcase = ({ playerId }: BadgeShowcaseProps) => {
style={{ style={{
width: '100px', width: '100px',
height: '100px', height: '100px',
borderRadius: '12px', borderRadius: 'var(--flxn-badge-radius)',
background: 'light-dark(var(--mantine-color-white), var(--mantine-color-dark-7))', background: 'var(--flxn-showcase-bg)',
border: '2px solid var(--mantine-primary-color-5)', border: '2px solid var(--mantine-primary-color-5)',
position: 'absolute', position: 'absolute',
top: `${(i + 1) * 4}px`, top: `${(i + 1) * 4}px`,
@@ -226,8 +226,8 @@ const BadgeShowcase = ({ playerId }: BadgeShowcaseProps) => {
style={{ style={{
width: '100px', width: '100px',
height: '100px', height: '100px',
borderRadius: '12px', borderRadius: 'var(--flxn-badge-radius)',
background: 'light-dark(var(--mantine-color-white), var(--mantine-color-dark-7))', background: 'var(--flxn-showcase-bg)',
border: display.earned border: display.earned
? '2px solid var(--mantine-primary-color-6)' ? '2px solid var(--mantine-primary-color-6)'
: '2px dashed var(--mantine-primary-color-4)', : '2px dashed var(--mantine-primary-color-4)',
@@ -9,7 +9,7 @@ export function BracketPending() {
p={0} p={0}
style={{ style={{
overflow: "hidden", overflow: "hidden",
backgroundImage: `radial-gradient(circle, var(--mantine-color-default-border) 1px, transparent 1px)`, backgroundImage: `radial-gradient(circle, var(--flxn-dotgrid-color) 1px, transparent 1px)`,
backgroundSize: "16px 16px", backgroundSize: "16px 16px",
backgroundPosition: "0 0, 8px 8px", backgroundPosition: "0 0, 8px 8px",
minHeight: "70dvh", minHeight: "70dvh",
@@ -89,7 +89,7 @@ const BracketView: React.FC<BracketViewProps> = ({ bracket, showControls, groupC
h={`calc(${height})`} h={`calc(${height})`}
className={styles["bracket-container"]} className={styles["bracket-container"]}
style={{ style={{
backgroundImage: `radial-gradient(circle, var(--mantine-color-default-border) 1px, transparent 1px)`, backgroundImage: `radial-gradient(circle, var(--flxn-dotgrid-color) 1px, transparent 1px)`,
backgroundSize: "16px 16px", backgroundSize: "16px 16px",
backgroundPosition: "0 0, 8px 8px", backgroundPosition: "0 0, 8px 8px",
}} }}
@@ -269,7 +269,11 @@ export const MatchCard: React.FC<MatchCardProps> = ({
w={showToolbar || showEditButton ? 200 : 220} w={showToolbar || showEditButton ? 200 : 220}
withBorder withBorder
pos="relative" pos="relative"
className={canTap ? styles["tappable-card"] : undefined} className={
canTap
? `flxn-bracket-card ${styles["tappable-card"]}`
: "flxn-bracket-card"
}
onClick={canTap ? () => onTap!(match) : undefined} onClick={canTap ? () => onTap!(match) : undefined}
role={canTap ? "button" : undefined} role={canTap ? "button" : undefined}
tabIndex={canTap ? 0 : undefined} tabIndex={canTap ? 0 : undefined}
@@ -83,10 +83,10 @@ export const MatchSlot: React.FC<MatchSlotProps> = ({
size={14} size={14}
weight="fill" weight="fill"
style={{ style={{
color: 'gold', color: 'var(--flxn-gold)',
marginLeft: '2px', marginLeft: '2px',
marginTop: '-1px', marginTop: '-1px',
filter: 'drop-shadow(0 1px 1px rgba(0,0,0,0.3))', filter: 'drop-shadow(0 1px 1px var(--flxn-pop-shadow))',
flexShrink: 0 flexShrink: 0
}} }}
/> />
@@ -52,6 +52,7 @@ const MatchCard = ({ match, hideH2H = false }: MatchCardProps) => {
py="md" py="md"
withBorder withBorder
radius="md" radius="md"
className="flxn-match-card"
style={{ position: "relative", zIndex: 2 }} style={{ position: "relative", zIndex: 2 }}
> >
<Stack gap="sm"> <Stack gap="sm">
@@ -208,16 +209,12 @@ const MatchCard = ({ match, hideH2H = false }: MatchCardProps) => {
<Paper <Paper
px="md" px="md"
pb={4} pb={4}
className="flxn-reaction-row"
style={{ style={{
position: "relative", position: "relative",
zIndex: 1, zIndex: 1,
marginTop: -6, marginTop: -6,
paddingTop: 8, paddingTop: 8,
backgroundColor: "var(--mantine-color-gray-light)",
borderTop: "none",
borderRadius:
"0 0 var(--mantine-radius-md) var(--mantine-radius-md)",
border: "1px solid var(--mantine-color-default-border)",
}} }}
> >
<Suspense> <Suspense>
@@ -130,7 +130,7 @@ const TeamHeadToHeadContent = ({ team1, team2, isOpen = true }: TeamHeadToHeadSh
{leader && ( {leader && (
<Group justify="center" gap="xs"> <Group justify="center" gap="xs">
<CrownIcon size={16} weight="fill" color="gold" /> <CrownIcon size={16} weight="fill" color="var(--flxn-gold)" />
<Text size="xs" c="dimmed"> <Text size="xs" c="dimmed">
<Trans>{leader.name} leads the series</Trans> <Trans>{leader.name} leads the series</Trans>
</Text> </Text>
@@ -158,7 +158,7 @@ const PlayerHeadToHeadContent = ({
{leader && ( {leader && (
<Group justify="center" gap="xs"> <Group justify="center" gap="xs">
<CrownIcon size={16} weight="fill" color="gold" /> <CrownIcon size={16} weight="fill" color="var(--flxn-gold)" />
<Text size="xs" c="dimmed"> <Text size="xs" c="dimmed">
<Trans>{leader} leads the series</Trans> <Trans>{leader} leads the series</Trans>
</Text> </Text>
@@ -1,4 +1,4 @@
import { Box, Stack, Text, Divider, Group, Button, Anchor } from "@mantine/core"; import { Box, Stack, Text, Divider, Group, Button } from "@mantine/core";
import { Suspense, useState, useDeferredValue } from "react"; import { Suspense, useState, useDeferredValue } from "react";
import { Trans, useLingui } from "@lingui/react/macro"; import { Trans, useLingui } from "@lingui/react/macro";
import { Link } from "@tanstack/react-router"; import { Link } from "@tanstack/react-router";
@@ -57,9 +57,14 @@ const Profile = ({ id }: ProfileProps) => {
<Stack px="md"> <Stack px="md">
<Group justify="space-between" align="center"> <Group justify="space-between" align="center">
<Text size="md" fw={700}><Trans>Badges</Trans></Text> <Text size="md" fw={700}><Trans>Badges</Trans></Text>
<Anchor component={Link} to="/badges" size="sm" fw={500}> <Button
component={Link}
to="/badges"
variant="subtle"
size="compact-sm"
>
<Trans>View all badges</Trans> <Trans>View all badges</Trans>
</Anchor> </Button>
</Group> </Group>
<Suspense fallback={<BadgeShowcaseSkeleton />}> <Suspense fallback={<BadgeShowcaseSkeleton />}>
<BadgeShowcase playerId={id} /> <BadgeShowcase playerId={id} />
@@ -244,7 +244,7 @@ export const PredictionLeaderboard: React.FC<PredictionLeaderboardProps> = ({
weight="fill" weight="fill"
color={ color={
entry.championCorrect && isComplete entry.championCorrect && isComplete
? "gold" ? "var(--flxn-gold)"
: "var(--mantine-color-dimmed)" : "var(--mantine-color-dimmed)"
} }
/> />
@@ -79,8 +79,8 @@ const TeamChip = ({
size={14} size={14}
weight="fill" weight="fill"
style={{ style={{
color: "gold", color: "var(--flxn-gold)",
filter: "drop-shadow(0 1px 1px rgba(0,0,0,0.3))", filter: "drop-shadow(0 1px 1px var(--flxn-pop-shadow))",
flexShrink: 0, flexShrink: 0,
}} }}
/> />
@@ -0,0 +1,111 @@
import {
Box,
SimpleGrid,
Stack,
Text,
UnstyledButton,
useComputedColorScheme,
} from "@mantine/core";
import { CaretRightIcon } from "@phosphor-icons/react";
import { Trans, useLingui } from "@lingui/react/macro";
import Sheet from "@/components/sheet/sheet";
import { useSheet } from "@/hooks/use-sheet";
import { updateUserTheme } from "@/features/settings/server";
import { useAuth } from "@/contexts/auth-context";
import { DEFAULT_THEME_ID, THEME_LIST, getTheme } from "@/themes/registry";
import ThemePreview from "./theme-preview";
const ThemePicker = () => {
const { metadata, user, set } = useAuth();
const { t } = useLingui();
const sheet = useSheet();
const scheme = useComputedColorScheme("dark");
const currentId = metadata.theme || DEFAULT_THEME_ID;
const current = getTheme(currentId);
const handleSelect = async (id: string) => {
if (!user || id === currentId) return;
const previous = metadata;
set({ metadata: { ...metadata, theme: id } });
try {
await updateUserTheme({ data: id });
} catch {
set({ metadata: previous });
}
};
return (
<Box>
<Text fw={500} size="sm" mb="xs">
<Trans>Theme</Trans>
</Text>
<UnstyledButton
onClick={sheet.open}
w="100%"
style={{
display: "flex",
alignItems: "center",
gap: "var(--mantine-spacing-sm)",
padding: "var(--mantine-spacing-xs)",
border: "1px solid var(--mantine-color-default-border)",
borderRadius: "var(--mantine-radius-md)",
}}
>
<Box w="3.25rem" style={{ flexShrink: 0 }}>
<ThemePreview
manifest={current}
scheme={scheme}
height="2.25rem"
scale={0.5}
/>
</Box>
<Text size="sm" fw={500} style={{ flex: 1, textAlign: "left" }}>
{t(current.label)}
</Text>
<CaretRightIcon size={16} color="var(--mantine-color-dimmed)" />
</UnstyledButton>
<Sheet title={t`Theme`} {...sheet.props}>
<SimpleGrid cols={3} spacing="sm" py="sm">
{[...THEME_LIST]
.sort(
(a, b) =>
(a.order ?? 100) - (b.order ?? 100) ||
t(a.label).localeCompare(t(b.label))
)
.map((manifest) => {
const isSelected = manifest.id === currentId;
return (
<UnstyledButton
key={manifest.id}
onClick={() => handleSelect(manifest.id)}
>
<Stack gap={6} align="center">
<Box
w="100%"
p="0.125rem"
bd={
isSelected
? "0.125rem solid var(--mantine-color-bright)"
: "0.125rem solid transparent"
}
style={{
borderRadius: "calc(var(--mantine-radius-md) + 0.25rem)",
}}
>
<ThemePreview manifest={manifest} scheme={scheme} />
</Box>
<Text size="xs" fw={isSelected ? 700 : 400} ta="center">
{t(manifest.label)}
</Text>
</Stack>
</UnstyledButton>
);
})}
</SimpleGrid>
</Sheet>
</Box>
);
};
export default ThemePicker;
@@ -0,0 +1,179 @@
import {
DEFAULT_THEME,
convertCssVariables,
defaultCssVariablesResolver,
mergeMantineTheme,
mergeThemeOverrides,
} from "@mantine/core";
import { useEffect, useMemo, useState } from "react";
import { useLingui } from "@lingui/react/macro";
import { useAuth } from "@/contexts/auth-context";
import { baseTheme } from "@/lib/mantine/base-theme";
import type { ThemeManifest } from "@/themes/types";
/* Each preview is a same-origin srcdoc iframe carrying the theme's own
data-theme/-scheme, the app's stylesheets, the theme's font links, and its
Mantine variables — 1:1 with the active theme, with nothing bleeding in. */
const buildMantineVarsCss = (manifest: ThemeManifest, accent: string) => {
const merged = mergeMantineTheme(
DEFAULT_THEME,
mergeThemeOverrides(baseTheme, manifest.mantine ?? {}, {
primaryColor: accent,
})
);
return convertCssVariables(defaultCssVariablesResolver(merged), ":root");
};
const collectAppCss = () =>
Array.from(document.querySelectorAll('style, link[rel="stylesheet"]'))
.filter(
(el) =>
!el.hasAttribute("data-mantine-styles") &&
!el.hasAttribute("data-flxn-theme-font")
)
.map((el) =>
el.tagName === "LINK"
? el.outerHTML
: `<style>${el.textContent ?? ""}</style>`
)
.join("\n");
const PREVIEW_LOCAL_CSS = `
html, body { margin: 0; height: 100%; overflow: hidden; }
body { display: flex; }
.pv-card {
flex: 1;
margin: 0.375rem;
padding: 0.5rem;
display: flex;
flex-direction: column;
justify-content: center;
gap: 6px;
background-color: var(--mantine-color-body);
border: 1px solid var(--mantine-color-default-border);
border-radius: var(--mantine-radius-default);
box-shadow: var(--mantine-shadow-sm);
}
.pv-heading {
font-family: var(--mantine-font-family-headings);
color: var(--mantine-color-text);
font-size: 0.875rem;
font-weight: 700;
line-height: 1;
}
.pv-row { display: flex; align-items: center; gap: 4px; }
.pv-chip {
width: 14px;
height: 14px;
flex-shrink: 0;
border-radius: var(--mantine-radius-default);
background-color: var(--mantine-primary-color-filled);
}
.pv-bars { display: flex; flex-direction: column; gap: 3px; flex: 1; }
.pv-bar {
display: block;
height: 4px;
border-radius: 2px;
background-color: var(--mantine-color-dimmed);
}
`;
const buildSrcdoc = (
manifest: ThemeManifest,
scheme: "light" | "dark",
accent: string,
appCss: string
) => {
const fontLinks = (manifest.fontLinks ?? [])
.map((href) => `<link rel="stylesheet" href="${href}">`)
.join("");
return `<!doctype html>
<html data-theme="${manifest.id}" data-mantine-color-scheme="${scheme}">
<head>
<meta charset="utf-8">
<style>html { color-scheme: ${scheme}; }</style>
<style>${buildMantineVarsCss(manifest, accent)}</style>
${appCss}
${fontLinks}
<style>${PREVIEW_LOCAL_CSS}</style>
</head>
<body>
<div class="pv-card">
<span class="pv-heading">Ag</span>
<div class="pv-row">
<span class="pv-chip"></span>
<span class="pv-bars">
<span class="pv-bar" style="opacity: 0.7"></span>
<span class="pv-bar" style="opacity: 0.4; width: 60%"></span>
</span>
</div>
</div>
</body>
</html>`;
};
interface ThemePreviewProps {
manifest: ThemeManifest;
scheme: "light" | "dark";
height?: string;
/** Renders the document at height/scale and scales it down visually. */
scale?: number;
}
const ThemePreview = ({
manifest,
scheme,
height = "4.5rem",
scale = 1,
}: ThemePreviewProps) => {
const { metadata } = useAuth();
const { t } = useLingui();
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
const previewScheme =
manifest.colorSchemes === "both" ? scheme : manifest.colorSchemes;
const accent = metadata.accentColor || "blue";
const srcdoc = useMemo(
() =>
mounted
? buildSrcdoc(manifest, previewScheme, accent, collectAppCss())
: null,
[mounted, manifest, previewScheme, accent]
);
const frameStyle: React.CSSProperties = {
width: "100%",
height,
border: "1px solid var(--mantine-color-default-border)",
borderRadius: "var(--mantine-radius-md)",
overflow: "hidden",
};
if (!srcdoc) return <div style={frameStyle} />;
return (
<div style={frameStyle}>
<iframe
srcDoc={srcdoc}
title={t(manifest.label)}
sandbox=""
scrolling="no"
aria-hidden
tabIndex={-1}
style={{
display: "block",
border: 0,
width: `${100 / scale}%`,
height: `calc(${height} / ${scale})`,
transform: `scale(${scale})`,
transformOrigin: "top left",
pointerEvents: "none",
}}
/>
</div>
);
};
export default ThemePreview;
+45
View File
@@ -2,12 +2,28 @@ import { createServerFn } from "@tanstack/react-start";
import { superTokensFunctionMiddleware } from "@/utils/supertokens"; import { superTokensFunctionMiddleware } from "@/utils/supertokens";
import { serverFnLoggingMiddleware } from "@/utils/activities"; import { serverFnLoggingMiddleware } from "@/utils/activities";
// Whitelists: these values flow into Mantine's primaryColor / the
// data-mantine-color-scheme attribute — an unknown value crash-loops or
// unstyles the account's UI on every load.
const ACCENT_COLORS = [
"blue",
"red",
"green",
"yellow",
"grape",
"orange",
"pink",
"lime",
];
const COLOR_SCHEMES = ["light", "dark", "auto"];
export const updateUserColorScheme = createServerFn({ method: "POST" }) export const updateUserColorScheme = createServerFn({ method: "POST" })
.validator((data: string) => data) .validator((data: string) => data)
.middleware([superTokensFunctionMiddleware, serverFnLoggingMiddleware]) .middleware([superTokensFunctionMiddleware, serverFnLoggingMiddleware])
.handler(async ({ context, data }) => { .handler(async ({ context, data }) => {
const { userAuthId, metadata } = context; const { userAuthId, metadata } = context;
if (!userAuthId) return; if (!userAuthId) return;
if (!COLOR_SCHEMES.includes(data)) return;
const { updateUserMetadataFields } = await import( const { updateUserMetadataFields } = await import(
"@/utils/supertokens-core.server" "@/utils/supertokens-core.server"
@@ -51,12 +67,41 @@ export const updateUserLocale = createServerFn({ method: "POST" })
}; };
}); });
export const updateUserTheme = createServerFn({ method: "POST" })
.validator((data: string) => data)
.middleware([superTokensFunctionMiddleware, serverFnLoggingMiddleware])
.handler(async ({ context, data }) => {
const { userAuthId, metadata } = context;
if (!userAuthId) return;
const { THEME_IDS } = await import("@/themes/registry");
if (!THEME_IDS.includes(data)) {
throw new Error("Unknown theme");
}
const { updateUserMetadataFields } = await import(
"@/utils/supertokens-core.server"
);
await updateUserMetadataFields(userAuthId, {
theme: data,
});
return {
metadata: {
...metadata,
theme: data,
},
};
});
export const updateUserAccentColor = createServerFn({ method: "POST" }) export const updateUserAccentColor = createServerFn({ method: "POST" })
.validator((data: string) => data) .validator((data: string) => data)
.middleware([superTokensFunctionMiddleware, serverFnLoggingMiddleware]) .middleware([superTokensFunctionMiddleware, serverFnLoggingMiddleware])
.handler(async ({ context, data }) => { .handler(async ({ context, data }) => {
const { userAuthId, metadata } = context; const { userAuthId, metadata } = context;
if (!userAuthId) return; if (!userAuthId) return;
if (!ACCENT_COLORS.includes(data)) return;
const { updateUserMetadataFields } = await import( const { updateUserMetadataFields } = await import(
"@/utils/supertokens-core.server" "@/utils/supertokens-core.server"
+9 -18
View File
@@ -14,26 +14,20 @@ type PodiumTier = "first" | "second" | "third";
const tierStyles: Record<PodiumTier, React.CSSProperties> = { const tierStyles: Record<PodiumTier, React.CSSProperties> = {
first: { first: {
background: background: "var(--flxn-podium-1-bg)",
"light-dark(color-mix(in srgb, var(--mantine-color-yellow-4) 10%, var(--mantine-color-white)), color-mix(in srgb, var(--mantine-color-yellow-8) 16%, var(--mantine-color-dark-6)))", border: "1px solid var(--flxn-podium-1-border)",
border:
"1px solid light-dark(color-mix(in srgb, var(--mantine-color-yellow-6) 45%, var(--mantine-color-gray-3)), color-mix(in srgb, var(--mantine-color-yellow-6) 35%, var(--mantine-color-dark-4)))",
borderRadius: "var(--mantine-radius-md)", borderRadius: "var(--mantine-radius-md)",
boxShadow: "var(--mantine-shadow-md)", boxShadow: "var(--mantine-shadow-md)",
}, },
second: { second: {
background: background: "var(--flxn-podium-2-bg)",
"light-dark(color-mix(in srgb, var(--mantine-color-gray-5) 10%, var(--mantine-color-white)), color-mix(in srgb, var(--mantine-color-gray-5) 10%, var(--mantine-color-dark-6)))", border: "1px solid var(--flxn-podium-2-border)",
border:
"1px solid light-dark(var(--mantine-color-gray-4), var(--mantine-color-dark-4))",
borderRadius: "var(--mantine-radius-md)", borderRadius: "var(--mantine-radius-md)",
boxShadow: "var(--mantine-shadow-xs)", boxShadow: "var(--mantine-shadow-xs)",
}, },
third: { third: {
background: background: "var(--flxn-podium-3-bg)",
"light-dark(color-mix(in srgb, var(--mantine-color-orange-5) 8%, var(--mantine-color-white)), color-mix(in srgb, var(--mantine-color-orange-9) 14%, var(--mantine-color-dark-6)))", border: "1px solid var(--flxn-podium-3-border)",
border:
"1px solid light-dark(color-mix(in srgb, var(--mantine-color-orange-6) 35%, var(--mantine-color-gray-3)), color-mix(in srgb, var(--mantine-color-orange-7) 30%, var(--mantine-color-dark-4)))",
borderRadius: "var(--mantine-radius-md)", borderRadius: "var(--mantine-radius-md)",
boxShadow: "var(--mantine-shadow-xs)", boxShadow: "var(--mantine-shadow-xs)",
}, },
@@ -54,8 +48,7 @@ const tierConfig: Record<
first: { first: {
color: "yellow", color: "yellow",
label: msg`Champions`, label: msg`Champions`,
labelColor: labelColor: "var(--flxn-podium-1-label)",
"light-dark(var(--mantine-color-yellow-8), var(--mantine-color-yellow-4))",
icon: <CrownIcon size={24} />, icon: <CrownIcon size={24} />,
iconSize: "xl", iconSize: "xl",
padding: "md", padding: "md",
@@ -64,8 +57,7 @@ const tierConfig: Record<
second: { second: {
color: "gray", color: "gray",
label: msg`2nd place`, label: msg`2nd place`,
labelColor: labelColor: "var(--flxn-podium-2-label)",
"light-dark(var(--mantine-color-gray-6), var(--mantine-color-gray-4))",
icon: <MedalIcon size={20} />, icon: <MedalIcon size={20} />,
iconSize: "lg", iconSize: "lg",
padding: "xs", padding: "xs",
@@ -74,8 +66,7 @@ const tierConfig: Record<
third: { third: {
color: "orange", color: "orange",
label: msg`3rd place`, label: msg`3rd place`,
labelColor: labelColor: "var(--flxn-podium-3-label)",
"light-dark(var(--mantine-color-orange-8), var(--mantine-color-orange-4))",
icon: <MedalIcon size={18} />, icon: <MedalIcon size={18} />,
iconSize: "lg", iconSize: "lg",
padding: "xs", padding: "xs",
@@ -320,7 +320,7 @@ const SetupGroupStage: React.FC<SetupGroupStageProps> = ({
</Text> </Text>
<Box <Box
style={{ style={{
backgroundImage: `radial-gradient(circle, var(--mantine-color-default-border) 1px, transparent 1px)`, backgroundImage: `radial-gradient(circle, var(--flxn-dotgrid-color) 1px, transparent 1px)`,
backgroundSize: "16px 16px", backgroundSize: "16px 16px",
backgroundPosition: "0 0, 8px 8px", backgroundPosition: "0 0, 8px 8px",
padding: "1rem", padding: "1rem",
@@ -11,6 +11,9 @@
height: 6px; height: 6px;
border-radius: 3px; border-radius: 3px;
background-color: var(--mantine-color-default-border); background-color: var(--mantine-color-default-border);
/* Mantine ships box-shadow: var(--mantine-shadow-sm) on indicators;
themes with hard offset shadow tokens render it as a doubled dot. */
box-shadow: none;
} }
.indicator.indicator[data-active] { .indicator.indicator[data-active] {
@@ -39,8 +39,12 @@ interface NavTileProps {
/** A single square-ish nav tile with icon-over-label and press feedback. */ /** A single square-ish nav tile with icon-over-label and press feedback. */
export const NavTile = ({ label, Icon, onClick, disabled }: NavTileProps) => ( export const NavTile = ({ label, Icon, onClick, disabled }: NavTileProps) => (
<UnstyledButton className={classes.tile} onClick={onClick} disabled={disabled}> <UnstyledButton
<span className={classes.icon}> className={`${classes.tile} flxn-tile`}
onClick={onClick}
disabled={disabled}
>
<span className={`${classes.icon} flxn-tile-icon`}>
<Icon size={22} weight="bold" /> <Icon size={22} weight="bold" />
</span> </span>
<Text size="xs" fw={600} lineClamp={2} lh={1.2}> <Text size="xs" fw={600} lineClamp={2} lh={1.2}>
+19 -6
View File
@@ -159,16 +159,29 @@ export function useServerEvents() {
logger.error("SSE connection error", error); logger.error("SSE connection error", error);
disconnect(); disconnect();
// The stream is auth-guarded, so an error is often the server rejecting
// an expired session rather than a network blip. Try to refresh; if the
// session is genuinely gone, stop retrying so we don't hammer the
// endpoint forever — a real re-login (or a wake event) will reconnect.
let sessionValid = true;
try {
const { attemptRefreshingSession } = await import('supertokens-web-js/recipe/session');
sessionValid = await attemptRefreshingSession();
} catch {
// Refresh call itself failed (likely offline) — treat as transient.
}
if (disposed) return;
if (!sessionValid) {
logger.warn("SSE stopped: session is no longer valid");
return;
}
retryCount += 1; retryCount += 1;
const delay = Math.min(1000 * Math.pow(1.5, retryCount - 1), 15000); const delay = Math.min(1000 * Math.pow(1.5, retryCount - 1), 15000);
logger.info(`SSE reconnection attempt ${retryCount} in ${Math.round(delay)}ms`); logger.info(`SSE reconnection attempt ${retryCount} in ${Math.round(delay)}ms`);
try {
const { attemptRefreshingSession } = await import('supertokens-web-js/recipe/session');
await attemptRefreshingSession();
} catch {
}
reconnect(delay); reconnect(delay);
}; };
}; };
+48
View File
@@ -0,0 +1,48 @@
import { createTheme } from "@mantine/core";
const commonInputStyles = {
label: {
padding: 5,
},
root: {
margin: "0",
},
};
// Shared by MantineProvider and the theme-picker preview iframes.
export const baseTheme = createTheme({
defaultRadius: "sm",
respectReducedMotion: true,
fontFamily: '"Inter", sans-serif',
headings: { fontFamily: '"League Spartan", sans-serif' },
activeClassName: "flxn-press",
components: {
TextInput: {
styles: commonInputStyles,
},
DateTimePicker: {
styles: commonInputStyles,
},
Input: {
styles: commonInputStyles,
},
Select: {
styles: commonInputStyles,
},
Autocomplete: {
styles: commonInputStyles,
},
Card: {
defaultProps: {
shadow: "xs",
},
},
Title: {
styles: {
root: {
letterSpacing: "-0.01em",
},
},
},
},
});
+29 -7
View File
@@ -1,18 +1,40 @@
import { useAuth } from "@/contexts/auth-context"; import { useAuth } from "@/contexts/auth-context";
import { useMantineColorScheme } from "@mantine/core"; import { useMantineColorScheme } from "@mantine/core";
import { useEffect } from "react"; import { useEffect, useRef } from "react";
import { CHROME_COLORS, setThemeColorMeta } from "./theme-colors"; import { getTheme } from "@/themes/registry";
import { resolveChromeColors } from "@/themes/chrome";
import { setBaseThemeColor } from "./theme-colors";
const ColorSchemeProvider = ({ children }: { children: React.ReactNode }) => { const ColorSchemeProvider = ({ children }: { children: React.ReactNode }) => {
const { const {
metadata: { colorScheme }, metadata: { colorScheme, theme },
} = useAuth(); } = useAuth();
const { setColorScheme } = useMantineColorScheme(); const { colorScheme: currentScheme, setColorScheme } =
useMantineColorScheme();
// Mantine recreates setColorScheme every render; calling it redundantly
// triggers its global transition-suppression flash, so ref it and only
// call on an actual value change.
const setColorSchemeRef = useRef(setColorScheme);
setColorSchemeRef.current = setColorScheme;
const manifest = getTheme(theme);
const forcedScheme =
manifest.colorSchemes === "both" ? undefined : manifest.colorSchemes;
useEffect(() => { useEffect(() => {
const chrome = resolveChromeColors(manifest);
if (forcedScheme) {
setBaseThemeColor(chrome[forcedScheme].base);
return;
}
if (!colorScheme) return; if (!colorScheme) return;
setColorScheme(colorScheme); if (colorScheme !== currentScheme) {
setColorSchemeRef.current(colorScheme);
}
const media = window.matchMedia("(prefers-color-scheme: dark)"); const media = window.matchMedia("(prefers-color-scheme: dark)");
const applyThemeColor = () => { const applyThemeColor = () => {
@@ -22,7 +44,7 @@ const ColorSchemeProvider = ({ children }: { children: React.ReactNode }) => {
? "dark" ? "dark"
: "light" : "light"
: colorScheme; : colorScheme;
setThemeColorMeta(CHROME_COLORS[resolved].base); setBaseThemeColor(chrome[resolved].base);
}; };
applyThemeColor(); applyThemeColor();
@@ -31,7 +53,7 @@ const ColorSchemeProvider = ({ children }: { children: React.ReactNode }) => {
media.addEventListener("change", applyThemeColor); media.addEventListener("change", applyThemeColor);
return () => media.removeEventListener("change", applyThemeColor); return () => media.removeEventListener("change", applyThemeColor);
} }
}, [colorScheme, setColorScheme]); }, [colorScheme, currentScheme, manifest, forcedScheme]);
return children; return children;
}; };
+18 -52
View File
@@ -1,12 +1,16 @@
import { useAuth } from "@/contexts/auth-context"; import { useAuth } from "@/contexts/auth-context";
import { import {
createTheme, mergeThemeOverrides,
MantineProvider as MantineProviderCore, MantineProvider as MantineProviderCore,
} from "@mantine/core"; } from "@mantine/core";
import { baseTheme } from "./base-theme";
import { DatesProvider } from "@mantine/dates"; import { DatesProvider } from "@mantine/dates";
import { useLingui } from "@lingui/react"; import { useLingui } from "@lingui/react";
import type { AppLocale } from "@/lib/i18n"; import type { AppLocale } from "@/lib/i18n";
import ColorSchemeProvider from "./color-scheme-provider"; import ColorSchemeProvider from "./color-scheme-provider";
import ThemeProvider from "@/themes/theme-provider";
import { DEFAULT_THEME_ID, getTheme } from "@/themes/registry";
import "@/themes/styles";
import { useState, useEffect, useMemo } from "react"; import { useState, useEffect, useMemo } from "react";
import "dayjs/locale/es-mx"; import "dayjs/locale/es-mx";
import "dayjs/locale/de"; import "dayjs/locale/de";
@@ -21,15 +25,6 @@ const DAYJS_LOCALE: Record<AppLocale, string> = {
ja: "ja", ja: "ja",
}; };
const commonInputStyles = {
label: {
padding: 5,
},
root: {
margin: "0",
},
};
const pressFeedbackCss = ` const pressFeedbackCss = `
.flxn-press { .flxn-press {
transition: transform 120ms cubic-bezier(0.32, 0.72, 0, 1); transition: transform 120ms cubic-bezier(0.32, 0.72, 0, 1);
@@ -46,43 +41,6 @@ const pressFeedbackCss = `
} }
`; `;
const theme = createTheme({
defaultRadius: "sm",
respectReducedMotion: true,
fontFamily: '"Inter", sans-serif',
headings: { fontFamily: '"League Spartan", sans-serif' },
activeClassName: "flxn-press",
components: {
TextInput: {
styles: commonInputStyles,
},
DateTimePicker: {
styles: commonInputStyles,
},
Input: {
styles: commonInputStyles,
},
Select: {
styles: commonInputStyles,
},
Autocomplete: {
styles: commonInputStyles,
},
Card: {
defaultProps: {
shadow: "xs",
},
},
Title: {
styles: {
root: {
letterSpacing: "-0.01em",
},
},
},
},
});
const MantineProvider = ({ children }: { children: React.ReactNode }) => { const MantineProvider = ({ children }: { children: React.ReactNode }) => {
const { metadata } = useAuth(); const { metadata } = useAuth();
const { i18n } = useLingui(); const { i18n } = useLingui();
@@ -93,17 +51,23 @@ const MantineProvider = ({ children }: { children: React.ReactNode }) => {
}, []); }, []);
const colorScheme = isHydrated ? metadata.colorScheme || "auto" : "auto"; const colorScheme = isHydrated ? metadata.colorScheme || "auto" : "auto";
const manifest = getTheme(isHydrated ? metadata.theme : DEFAULT_THEME_ID);
const primaryColor = isHydrated ? metadata.accentColor || "blue" : "blue"; const primaryColor = isHydrated ? metadata.accentColor || "blue" : "blue";
const themeWithPrimaryColor = useMemo( const theme = useMemo(
() => ({ ...theme, primaryColor }), () =>
[primaryColor] mergeThemeOverrides(baseTheme, manifest.mantine ?? {}, { primaryColor }),
[manifest, primaryColor]
); );
const forceColorScheme =
manifest.colorSchemes === "both" ? undefined : manifest.colorSchemes;
return ( return (
<MantineProviderCore <MantineProviderCore
defaultColorScheme={colorScheme} defaultColorScheme={colorScheme}
theme={themeWithPrimaryColor} forceColorScheme={forceColorScheme}
theme={theme}
> >
<style href="flxn-press-feedback" precedence="medium"> <style href="flxn-press-feedback" precedence="medium">
{pressFeedbackCss} {pressFeedbackCss}
@@ -113,7 +77,9 @@ const MantineProvider = ({ children }: { children: React.ReactNode }) => {
locale: DAYJS_LOCALE[i18n.locale as AppLocale] ?? "en", locale: DAYJS_LOCALE[i18n.locale as AppLocale] ?? "en",
}} }}
> >
<ColorSchemeProvider>{children}</ColorSchemeProvider> <ThemeProvider manifest={manifest} ready={isHydrated}>
<ColorSchemeProvider>{children}</ColorSchemeProvider>
</ThemeProvider>
</DatesProvider> </DatesProvider>
</MantineProviderCore> </MantineProviderCore>
); );
+18
View File
@@ -8,6 +8,24 @@ export const CHROME_COLORS: Record<
dark: { base: "#242424", dimmed: "#161616" }, dark: { base: "#242424", dimmed: "#161616" },
}; };
// theme-color meta ownership: while a drawer holds a dimmed color, base
// writes (from scheme/theme changes) must not overwrite it.
let dimmedDepth = 0;
export function setBaseThemeColor(color: string): void {
if (dimmedDepth === 0) setThemeColorMeta(color);
}
export function pushDimmedThemeColor(color: string): void {
dimmedDepth++;
setThemeColorMeta(color);
}
export function popDimmedThemeColor(base: string): void {
dimmedDepth = Math.max(0, dimmedDepth - 1);
if (dimmedDepth === 0) setThemeColorMeta(base);
}
export function setThemeColorMeta(color: string): void { export function setThemeColorMeta(color: string): void {
if (typeof document === "undefined") return; if (typeof document === "undefined") return;
+2 -2
View File
@@ -21,7 +21,7 @@ function success(toast: Omit<ToastProps, "id"> | string) {
const config = typeof toast === "string" ? { description: toast } : toast; const config = typeof toast === "string" ? { description: toast } : toast;
return makeToast({ return makeToast({
...config, ...config,
icon: <ShieldCheckIcon color="lightgreen" size={48} weight="fill" />, icon: <ShieldCheckIcon color="var(--flxn-toast-success)" size={48} weight="fill" />,
}); });
} }
@@ -29,7 +29,7 @@ function error(toast: Omit<ToastProps, "id"> | string) {
const config = typeof toast === "string" ? { description: toast } : toast; const config = typeof toast === "string" ? { description: toast } : toast;
return makeToast({ return makeToast({
...config, ...config,
icon: <WarningCircleIcon color="lightcoral" size={48} weight="fill" />, icon: <WarningCircleIcon color="var(--flxn-toast-error)" size={48} weight="fill" />,
}); });
} }
+106 -33
View File
@@ -13,7 +13,7 @@ msgstr ""
"Language-Team: \n" "Language-Team: \n"
"Plural-Forms: \n" "Plural-Forms: \n"
#: src/features/matches/components/match-card.tsx:77 #: src/features/matches/components/match-card.tsx:78
msgid " (Losers)" msgid " (Losers)"
msgstr " (Verlierer)" msgstr " (Verlierer)"
@@ -140,7 +140,7 @@ msgstr "{0}ms"
msgid "{count, plural, one {Players (#)} other {Players (#)}}" msgid "{count, plural, one {Players (#)} other {Players (#)}}"
msgstr "{count, plural, one {Spieler (#)} other {Spieler (#)}}" msgstr "{count, plural, one {Spieler (#)} other {Spieler (#)}}"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:130 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:134
#: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:24 #: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:24
msgid "{count, plural, one {Teams (#)} other {Teams (#)}}" msgid "{count, plural, one {Teams (#)} other {Teams (#)}}"
msgstr "{count, plural, one {Teams (#)} other {Teams (#)}}" msgstr "{count, plural, one {Teams (#)} other {Teams (#)}}"
@@ -201,7 +201,7 @@ msgstr "{size}-Team-K.-o.-Runde"
msgid "* Confidence penalty applied for players with <15 matches" msgid "* Confidence penalty applied for players with <15 matches"
msgstr "* Vertrauensabschlag für Spieler mit <15 Spielen" msgstr "* Vertrauensabschlag für Spieler mit <15 Spielen"
#: src/features/bracket/components/match-card.tsx:320 #: src/features/bracket/components/match-card.tsx:324
#: src/features/predictions/components/prediction-match-card.tsx:108 #: src/features/predictions/components/prediction-match-card.tsx:108
msgid "* If necessary" msgid "* If necessary"
msgstr "* Falls nötig" msgstr "* Falls nötig"
@@ -327,7 +327,7 @@ msgstr "1 s"
msgid "2. Earlier submission" msgid "2. Earlier submission"
msgstr "2. Frühere Abgabe" msgstr "2. Frühere Abgabe"
#: src/features/tournaments/components/podium.tsx:66 #: src/features/tournaments/components/podium.tsx:59
msgid "2nd place" msgid "2nd place"
msgstr "2. Platz" msgstr "2. Platz"
@@ -335,7 +335,7 @@ msgstr "2. Platz"
msgid "30s" msgid "30s"
msgstr "30 s" msgstr "30 s"
#: src/features/tournaments/components/podium.tsx:76 #: src/features/tournaments/components/podium.tsx:68
msgid "3rd place" msgid "3rd place"
msgstr "3. Platz" msgstr "3. Platz"
@@ -406,6 +406,10 @@ msgstr "Team hinzufügen"
msgid "Admin" msgid "Admin"
msgstr "Admin" msgstr "Admin"
#: src/themes/aero/manifest.ts:117
msgid "Aero"
msgstr "Aero"
#: src/app/routes/_authed/stats.tsx:59 #: src/app/routes/_authed/stats.tsx:59
#: src/features/activities/components/activities-table.tsx:328 #: src/features/activities/components/activities-table.tsx:328
#: src/features/players/components/profile/index.tsx:28 #: src/features/players/components/profile/index.tsx:28
@@ -436,11 +440,11 @@ msgstr "Beim Senden eines Einmal-Codes ist ein unerwarteter Fehler aufgetreten.
msgid "An unexpected error occurred. Please try again." msgid "An unexpected error occurred. Please try again."
msgstr "Ein unerwarteter Fehler ist aufgetreten. Bitte versuche es erneut." msgstr "Ein unerwarteter Fehler ist aufgetreten. Bitte versuche es erneut."
#: src/features/bracket/components/match-card.tsx:336 #: src/features/bracket/components/match-card.tsx:340
msgid "Announce matchup" msgid "Announce matchup"
msgstr "Matchup ansagen" msgstr "Matchup ansagen"
#: src/app/routes/_authed/settings.tsx:34 #: src/app/routes/_authed/settings.tsx:35
msgid "Appearance" msgid "Appearance"
msgstr "Erscheinungsbild" msgstr "Erscheinungsbild"
@@ -452,6 +456,10 @@ msgstr "Bist du sicher, dass du dich von diesem Turnier abmelden möchtest? Du k
msgid "Arguments" msgid "Arguments"
msgstr "Argumente" msgstr "Argumente"
#: src/themes/deco/manifest.ts:6
msgid "Art Deco"
msgstr "Art déco"
#: src/features/tournaments/components/manage-tournament.tsx:69 #: src/features/tournaments/components/manage-tournament.tsx:69
msgid "Assign Partners" msgid "Assign Partners"
msgstr "Partner zuweisen" msgstr "Partner zuweisen"
@@ -554,12 +562,20 @@ msgstr "Abzeichentyp"
msgid "Badges" msgid "Badges"
msgstr "Abzeichen" msgstr "Abzeichen"
#: src/themes/bauhaus/manifest.ts:6
msgid "Bauhaus"
msgstr "Bauhaus"
#: src/themes/circus/manifest.ts:141
msgid "Big Top"
msgstr "Zirkus"
#: src/features/bracket/components/match-form.tsx:43 #: src/features/bracket/components/match-form.tsx:43
#: src/features/bracket/components/match-form.tsx:60 #: src/features/bracket/components/match-form.tsx:60
msgid "Both teams cannot have 10 cups" msgid "Both teams cannot have 10 cups"
msgstr "Beide Teams können nicht 10 Becher haben" msgstr "Beide Teams können nicht 10 Becher haben"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:102 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:106
#: src/features/tournaments/components/upcoming-tournament/index.tsx:150 #: src/features/tournaments/components/upcoming-tournament/index.tsx:150
msgid "Bracket" msgid "Bracket"
msgstr "Turnierbaum" msgstr "Turnierbaum"
@@ -573,6 +589,10 @@ msgstr "Turnierbaum-Vorschau"
msgid "Bracket Reset" msgid "Bracket Reset"
msgstr "Bracket-Reset" msgstr "Bracket-Reset"
#: src/themes/brutal/manifest.ts:6
msgid "Brutal"
msgstr "Brutal"
#: src/features/players/components/player-stats-table.tsx:139 #: src/features/players/components/player-stats-table.tsx:139
#: src/features/tournaments/components/tournament-stats.tsx:152 #: src/features/tournaments/components/tournament-stats.tsx:152
msgid "CA" msgid "CA"
@@ -605,12 +625,16 @@ msgstr "Teams können nicht mit einer ungeraden Anzahl an Spielern erstellt werd
msgid "Capture current state" msgid "Capture current state"
msgstr "Aktuellen Status erfassen" msgstr "Aktuellen Status erfassen"
#: src/themes/cassette/manifest.ts:145
msgid "Cassette Futurism"
msgstr "Retro-Tech"
#: src/features/players/components/player-stats-table.tsx:138 #: src/features/players/components/player-stats-table.tsx:138
#: src/features/tournaments/components/tournament-stats.tsx:144 #: src/features/tournaments/components/tournament-stats.tsx:144
msgid "CF" msgid "CF"
msgstr "CF" msgstr "CF"
#: src/features/tournaments/components/podium.tsx:56 #: src/features/tournaments/components/podium.tsx:50
msgid "Champions" msgid "Champions"
msgstr "Champions" msgstr "Champions"
@@ -623,17 +647,21 @@ msgstr "Logo ändern"
msgid "Choose a player" msgid "Choose a player"
msgstr "Wähle einen Spieler" msgstr "Wähle einen Spieler"
#: src/themes/clay/manifest.ts:144
msgid "Claymation"
msgstr "Knete"
#: src/features/players/components/league-head-to-head.tsx:99 #: src/features/players/components/league-head-to-head.tsx:99
msgid "Clear selected player" msgid "Clear selected player"
msgstr "Ausgewählten Spieler entfernen" msgstr "Ausgewählten Spieler entfernen"
#: src/components/sheet/modal.tsx:35 #: src/components/sheet/modal.tsx:35
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:165 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:169
#: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:36 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:36
msgid "Close" msgid "Close"
msgstr "Schließen" msgstr "Schließen"
#: src/components/avatar.tsx:107 #: src/components/avatar.tsx:127
msgid "Close image preview" msgid "Close image preview"
msgstr "Bildvorschau schließen" msgstr "Bildvorschau schließen"
@@ -785,10 +813,22 @@ msgstr "Benutzerdefinierter Hex-Code"
msgid "Dark" msgid "Dark"
msgstr "Dunkel" msgstr "Dunkel"
#: src/themes/gothic/manifest.ts:6
msgid "Dark Academia"
msgstr "Dark Academia"
#: src/features/activities/components/activities-table.tsx:356 #: src/features/activities/components/activities-table.tsx:356
msgid "Date" msgid "Date"
msgstr "Datum" msgstr "Datum"
#: src/themes/ocean/manifest.ts:117
msgid "Deep Sea"
msgstr "Tiefsee"
#: src/themes/default/manifest.ts:6
msgid "Default"
msgstr "Standard"
#: src/features/tournaments/components/tournament-form.tsx:150 #: src/features/tournaments/components/tournament-form.tsx:150
msgid "Description" msgid "Description"
msgstr "Beschreibung" msgstr "Beschreibung"
@@ -833,16 +873,16 @@ msgstr "Angemeldete Teams bearbeiten"
msgid "Edit Enrollments" msgid "Edit Enrollments"
msgstr "Anmeldungen bearbeiten" msgstr "Anmeldungen bearbeiten"
#: src/features/bracket/components/match-card.tsx:388 #: src/features/bracket/components/match-card.tsx:392
msgid "Edit Match" msgid "Edit Match"
msgstr "Match bearbeiten" msgstr "Match bearbeiten"
#: src/features/bracket/components/match-card.tsx:376 #: src/features/bracket/components/match-card.tsx:380
#: src/features/tournaments/components/group-match-card.tsx:187 #: src/features/tournaments/components/group-match-card.tsx:187
msgid "Edit match score" msgid "Edit match score"
msgstr "Spielstand bearbeiten" msgstr "Spielstand bearbeiten"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:110 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:114
msgid "Edit Prediction" msgid "Edit Prediction"
msgstr "Tipp bearbeiten" msgstr "Tipp bearbeiten"
@@ -914,7 +954,7 @@ msgid "Enrolled Players"
msgstr "Angemeldete Spieler" msgstr "Angemeldete Spieler"
#: src/features/tournaments/components/edit-enrolled-teams.tsx:151 #: src/features/tournaments/components/edit-enrolled-teams.tsx:151
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:141 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:145
#: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:31 #: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:31
msgid "Enrolled Teams" msgid "Enrolled Teams"
msgstr "Angemeldete Teams" msgstr "Angemeldete Teams"
@@ -1104,6 +1144,10 @@ msgstr "Zurück"
msgid "Go Back" msgid "Go Back"
msgstr "Zurück" msgstr "Zurück"
#: src/themes/seventies/manifest.ts:6
msgid "Groovy"
msgstr "Groovy"
#. placeholder {0}: group.groupName #. placeholder {0}: group.groupName
#. placeholder {0}: group.name #. placeholder {0}: group.name
#: src/features/tournaments/components/group-preview.tsx:24 #: src/features/tournaments/components/group-preview.tsx:24
@@ -1138,7 +1182,7 @@ msgstr "Gruppenphase erfolgreich erstellt!"
msgid "Group Stage Setup" msgid "Group Stage Setup"
msgstr "Gruppenphase einrichten" msgstr "Gruppenphase einrichten"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:94 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:98
msgid "Groups" msgid "Groups"
msgstr "Gruppen" msgstr "Gruppen"
@@ -1154,8 +1198,8 @@ msgstr "Hast du schon mal mitgespielt?"
#: src/app/routes/_authed/stats.tsx:47 #: src/app/routes/_authed/stats.tsx:47
#: src/features/bracket/components/match-dock.tsx:173 #: src/features/bracket/components/match-dock.tsx:173
#: src/features/bracket/components/match-dock.tsx:215 #: src/features/bracket/components/match-dock.tsx:215
#: src/features/matches/components/match-card.tsx:83 #: src/features/matches/components/match-card.tsx:84
#: src/features/matches/components/match-card.tsx:231 #: src/features/matches/components/match-card.tsx:228
#: src/features/players/components/league-head-to-head.tsx:183 #: src/features/players/components/league-head-to-head.tsx:183
#: src/features/players/components/profile/header.tsx:90 #: src/features/players/components/profile/header.tsx:90
msgid "Head to Head" msgid "Head to Head"
@@ -1339,7 +1383,7 @@ msgstr "S"
msgid "Mainline" msgid "Mainline"
msgstr "Mainline" msgstr "Mainline"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:110 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:114
msgid "Make Prediction" msgid "Make Prediction"
msgstr "Tipp abgeben" msgstr "Tipp abgeben"
@@ -1347,7 +1391,7 @@ msgstr "Tipp abgeben"
msgid "Make Your Prediction" msgid "Make Your Prediction"
msgstr "Gib deinen Tipp ab" msgstr "Gib deinen Tipp ab"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:85 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:89
#: src/features/tournaments/components/upcoming-tournament/index.tsx:143 #: src/features/tournaments/components/upcoming-tournament/index.tsx:143
msgid "Manage" msgid "Manage"
msgstr "Verwalten" msgstr "Verwalten"
@@ -1392,7 +1436,7 @@ msgstr "Match {0} · Runde {1} (Verlierer)"
#. placeholder {0}: match.order #. placeholder {0}: match.order
#. placeholder {1}: match.home!.name #. placeholder {1}: match.home!.name
#. placeholder {2}: match.away!.name #. placeholder {2}: match.away!.name
#: src/features/bracket/components/match-card.tsx:288 #: src/features/bracket/components/match-card.tsx:292
msgid "Match {0}: {1} vs {2}" msgid "Match {0}: {1} vs {2}"
msgstr "Match {0}: {1} vs {2}" msgstr "Match {0}: {1} vs {2}"
@@ -1415,7 +1459,7 @@ msgid "Match started!"
msgstr "Match gestartet!" msgstr "Match gestartet!"
#: src/features/players/components/player-stats-table.tsx:366 #: src/features/players/components/player-stats-table.tsx:366
#: src/features/players/components/profile/index.tsx:73 #: src/features/players/components/profile/index.tsx:78
#: src/features/players/components/profile/skeleton.tsx:70 #: src/features/players/components/profile/skeleton.tsx:70
#: src/features/teams/components/team-profile/index.tsx:44 #: src/features/teams/components/team-profile/index.tsx:44
#: src/features/teams/components/team-profile/skeleton.tsx:75 #: src/features/teams/components/team-profile/skeleton.tsx:75
@@ -1489,6 +1533,10 @@ msgstr "Nerd"
msgid "Network error. Please check your connection and try again." msgid "Network error. Please check your connection and try again."
msgstr "Netzwerkfehler. Bitte überprüfe deine Verbindung und versuche es erneut." msgstr "Netzwerkfehler. Bitte überprüfe deine Verbindung und versuche es erneut."
#: src/themes/neumorph/manifest.ts:6
msgid "Neumorphic"
msgstr "Neumorph"
#: src/features/players/components/players-activity-table.tsx:27 #: src/features/players/components/players-activity-table.tsx:27
msgid "Never" msgid "Never"
msgstr "Nie" msgstr "Nie"
@@ -1834,7 +1882,7 @@ msgstr "Punktevergabe für Tipps:"
#: src/app/routes/_authed/tournaments/$id.predictions.tsx:23 #: src/app/routes/_authed/tournaments/$id.predictions.tsx:23
#: src/features/predictions/components/prediction-leaderboard.tsx:147 #: src/features/predictions/components/prediction-leaderboard.tsx:147
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:119 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:123
msgid "Predictions" msgid "Predictions"
msgstr "Tipps" msgstr "Tipps"
@@ -1867,6 +1915,10 @@ msgstr "PKT"
msgid "Q" msgid "Q"
msgstr "Q" msgstr "Q"
#: src/themes/quest/manifest.ts:6
msgid "Quest Log"
msgstr "Questlog"
#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:167 #: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:167
msgid "Re-roll" msgid "Re-roll"
msgstr "Neu würfeln" msgstr "Neu würfeln"
@@ -1923,11 +1975,11 @@ msgid "rofl"
msgstr "rofl" msgstr "rofl"
#. placeholder {0}: match.round + 1 #. placeholder {0}: match.round + 1
#: src/features/matches/components/match-card.tsx:76 #: src/features/matches/components/match-card.tsx:77
msgid "Round {0}" msgid "Round {0}"
msgstr "Runde {0}" msgstr "Runde {0}"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:138 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:142
#: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23
msgid "Rules" msgid "Rules"
msgstr "Regeln" msgstr "Regeln"
@@ -2099,7 +2151,7 @@ msgstr "Serverfunktionen"
msgid "serverFn name" msgid "serverFn name"
msgstr "serverFn-Name" msgstr "serverFn-Name"
#: src/app/routes/_authed/settings.tsx:15 #: src/app/routes/_authed/settings.tsx:16
#: src/features/core/components/settings-button.tsx:19 #: src/features/core/components/settings-button.tsx:19
msgid "Settings" msgid "Settings"
msgstr "Einstellungen" msgstr "Einstellungen"
@@ -2118,10 +2170,18 @@ msgstr "{0} von {1} Spielern angezeigt"
msgid "Shuffle Groups" msgid "Shuffle Groups"
msgstr "Gruppen mischen" msgstr "Gruppen mischen"
#: src/app/routes/_authed/settings.tsx:42 #: src/app/routes/_authed/settings.tsx:44
msgid "Sign Out" msgid "Sign Out"
msgstr "Abmelden" msgstr "Abmelden"
#: src/themes/sketch/manifest.ts:6
msgid "Sketchbook"
msgstr "Skizzenbuch"
#: src/themes/skeuo/manifest.ts:6
msgid "Skeuomorphic"
msgstr "Skeuomorph"
#: src/components/DefaultCatchBoundary.tsx:89 #: src/components/DefaultCatchBoundary.tsx:89
msgid "Something went wrong" msgid "Something went wrong"
msgstr "Etwas ist schiefgelaufen" msgstr "Etwas ist schiefgelaufen"
@@ -2188,7 +2248,7 @@ msgstr "Start"
msgid "Start Date" msgid "Start Date"
msgstr "Startdatum" msgstr "Startdatum"
#: src/features/bracket/components/match-card.tsx:354 #: src/features/bracket/components/match-card.tsx:358
#: src/features/tournaments/components/group-match-card.tsx:168 #: src/features/tournaments/components/group-match-card.tsx:168
msgid "Start match" msgid "Start match"
msgstr "Match starten" msgstr "Match starten"
@@ -2316,11 +2376,11 @@ msgstr "Team aktualisiert, aber Logo-Upload fehlgeschlagen: {0}"
msgid "Team updated successfully!" msgid "Team updated successfully!"
msgstr "Team erfolgreich aktualisiert!" msgstr "Team erfolgreich aktualisiert!"
#: src/features/players/components/profile/index.tsx:78 #: src/features/players/components/profile/index.tsx:83
#: src/features/players/components/profile/skeleton.tsx:75 #: src/features/players/components/profile/skeleton.tsx:75
#: src/features/tournaments/components/profile/index.tsx:32 #: src/features/tournaments/components/profile/index.tsx:32
#: src/features/tournaments/components/profile/skeleton.tsx:89 #: src/features/tournaments/components/profile/skeleton.tsx:89
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:133 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:137
msgid "Teams" msgid "Teams"
msgstr "Teams" msgstr "Teams"
@@ -2328,6 +2388,10 @@ msgstr "Teams"
msgid "Teams created successfully!" msgid "Teams created successfully!"
msgstr "Teams erfolgreich erstellt!" msgstr "Teams erfolgreich erstellt!"
#: src/themes/terminal/manifest.ts:6
msgid "Terminal"
msgstr "Terminal"
#: src/app/routes/api/push/test.ts:29 #: src/app/routes/api/push/test.ts:29
msgid "Test notification — push is working on this device." msgid "Test notification — push is working on this device."
msgstr "Test-Benachrichtigung — Push funktioniert auf diesem Gerät." msgstr "Test-Benachrichtigung — Push funktioniert auf diesem Gerät."
@@ -2352,6 +2416,11 @@ msgstr "Das andere Team"
msgid "The requested resource was not found" msgid "The requested resource was not found"
msgstr "Die angeforderte Ressource wurde nicht gefunden" msgstr "Die angeforderte Ressource wurde nicht gefunden"
#: src/features/settings/components/theme-picker.tsx:40
#: src/features/settings/components/theme-picker.tsx:68
msgid "Theme"
msgstr "Design"
#: src/features/players/components/player-head-to-head-sheet.tsx:107 #: src/features/players/components/player-head-to-head-sheet.tsx:107
msgid "These players have not faced each other yet." msgid "These players have not faced each other yet."
msgstr "Diese Spieler sind noch nicht gegeneinander angetreten." msgstr "Diese Spieler sind noch nicht gegeneinander angetreten."
@@ -2421,7 +2490,7 @@ msgstr "Turnier erfolgreich erstellt!"
msgid "Tournament not found" msgid "Tournament not found"
msgstr "Turnier nicht gefunden" msgstr "Turnier nicht gefunden"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:148 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:152
#: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:25 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:25
msgid "Tournament Rules" msgid "Tournament Rules"
msgstr "Turnierregeln" msgstr "Turnierregeln"
@@ -2512,7 +2581,7 @@ msgstr "User-Agent"
msgid "versus" msgid "versus"
msgstr "gegen" msgstr "gegen"
#: src/features/players/components/profile/index.tsx:61 #: src/features/players/components/profile/index.tsx:66
msgid "View all badges" msgid "View all badges"
msgstr "Alle Abzeichen ansehen" msgstr "Alle Abzeichen ansehen"
@@ -2529,7 +2598,7 @@ msgid "View Groups"
msgstr "Gruppen ansehen" msgstr "Gruppen ansehen"
#: src/features/bracket/components/match-dock.tsx:178 #: src/features/bracket/components/match-dock.tsx:178
#: src/features/matches/components/match-card.tsx:88 #: src/features/matches/components/match-card.tsx:89
msgid "View head-to-head" msgid "View head-to-head"
msgstr "Direktvergleich ansehen" msgstr "Direktvergleich ansehen"
@@ -2537,6 +2606,10 @@ msgstr "Direktvergleich ansehen"
msgid "View Predictions" msgid "View Predictions"
msgstr "Tipps ansehen" msgstr "Tipps ansehen"
#: src/themes/wireframe/manifest.ts:118
msgid "Viewport"
msgstr "Viewport"
#: src/app/routes/api/spotify/playback.ts:70 #: src/app/routes/api/spotify/playback.ts:70
msgid "volumePercent must be a number" msgid "volumePercent must be a number"
msgstr "volumePercent muss eine Zahl sein" msgstr "volumePercent muss eine Zahl sein"
+106 -33
View File
@@ -13,7 +13,7 @@ msgstr ""
"Language-Team: \n" "Language-Team: \n"
"Plural-Forms: \n" "Plural-Forms: \n"
#: src/features/matches/components/match-card.tsx:77 #: src/features/matches/components/match-card.tsx:78
msgid " (Losers)" msgid " (Losers)"
msgstr " (Losers)" msgstr " (Losers)"
@@ -140,7 +140,7 @@ msgstr "{0}ms"
msgid "{count, plural, one {Players (#)} other {Players (#)}}" msgid "{count, plural, one {Players (#)} other {Players (#)}}"
msgstr "{count, plural, one {Players (#)} other {Players (#)}}" msgstr "{count, plural, one {Players (#)} other {Players (#)}}"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:130 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:134
#: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:24 #: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:24
msgid "{count, plural, one {Teams (#)} other {Teams (#)}}" msgid "{count, plural, one {Teams (#)} other {Teams (#)}}"
msgstr "{count, plural, one {Teams (#)} other {Teams (#)}}" msgstr "{count, plural, one {Teams (#)} other {Teams (#)}}"
@@ -201,7 +201,7 @@ msgstr "{size}-team knockout"
msgid "* Confidence penalty applied for players with <15 matches" msgid "* Confidence penalty applied for players with <15 matches"
msgstr "* Confidence penalty applied for players with <15 matches" msgstr "* Confidence penalty applied for players with <15 matches"
#: src/features/bracket/components/match-card.tsx:320 #: src/features/bracket/components/match-card.tsx:324
#: src/features/predictions/components/prediction-match-card.tsx:108 #: src/features/predictions/components/prediction-match-card.tsx:108
msgid "* If necessary" msgid "* If necessary"
msgstr "* If necessary" msgstr "* If necessary"
@@ -327,7 +327,7 @@ msgstr "1s"
msgid "2. Earlier submission" msgid "2. Earlier submission"
msgstr "2. Earlier submission" msgstr "2. Earlier submission"
#: src/features/tournaments/components/podium.tsx:66 #: src/features/tournaments/components/podium.tsx:59
msgid "2nd place" msgid "2nd place"
msgstr "2nd place" msgstr "2nd place"
@@ -335,7 +335,7 @@ msgstr "2nd place"
msgid "30s" msgid "30s"
msgstr "30s" msgstr "30s"
#: src/features/tournaments/components/podium.tsx:76 #: src/features/tournaments/components/podium.tsx:68
msgid "3rd place" msgid "3rd place"
msgstr "3rd place" msgstr "3rd place"
@@ -406,6 +406,10 @@ msgstr "Add Team"
msgid "Admin" msgid "Admin"
msgstr "Admin" msgstr "Admin"
#: src/themes/aero/manifest.ts:117
msgid "Aero"
msgstr "Aero"
#: src/app/routes/_authed/stats.tsx:59 #: src/app/routes/_authed/stats.tsx:59
#: src/features/activities/components/activities-table.tsx:328 #: src/features/activities/components/activities-table.tsx:328
#: src/features/players/components/profile/index.tsx:28 #: src/features/players/components/profile/index.tsx:28
@@ -436,11 +440,11 @@ msgstr "An unexpected error occurred when trying to send a one-time passcode. Pl
msgid "An unexpected error occurred. Please try again." msgid "An unexpected error occurred. Please try again."
msgstr "An unexpected error occurred. Please try again." msgstr "An unexpected error occurred. Please try again."
#: src/features/bracket/components/match-card.tsx:336 #: src/features/bracket/components/match-card.tsx:340
msgid "Announce matchup" msgid "Announce matchup"
msgstr "Announce matchup" msgstr "Announce matchup"
#: src/app/routes/_authed/settings.tsx:34 #: src/app/routes/_authed/settings.tsx:35
msgid "Appearance" msgid "Appearance"
msgstr "Appearance" msgstr "Appearance"
@@ -452,6 +456,10 @@ msgstr "Are you sure you want to unenroll from this tournament? You can enroll a
msgid "Arguments" msgid "Arguments"
msgstr "Arguments" msgstr "Arguments"
#: src/themes/deco/manifest.ts:6
msgid "Art Deco"
msgstr "Art Deco"
#: src/features/tournaments/components/manage-tournament.tsx:69 #: src/features/tournaments/components/manage-tournament.tsx:69
msgid "Assign Partners" msgid "Assign Partners"
msgstr "Assign Partners" msgstr "Assign Partners"
@@ -554,12 +562,20 @@ msgstr "Badge Type"
msgid "Badges" msgid "Badges"
msgstr "Badges" msgstr "Badges"
#: src/themes/bauhaus/manifest.ts:6
msgid "Bauhaus"
msgstr "Bauhaus"
#: src/themes/circus/manifest.ts:141
msgid "Big Top"
msgstr "Big Top"
#: src/features/bracket/components/match-form.tsx:43 #: src/features/bracket/components/match-form.tsx:43
#: src/features/bracket/components/match-form.tsx:60 #: src/features/bracket/components/match-form.tsx:60
msgid "Both teams cannot have 10 cups" msgid "Both teams cannot have 10 cups"
msgstr "Both teams cannot have 10 cups" msgstr "Both teams cannot have 10 cups"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:102 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:106
#: src/features/tournaments/components/upcoming-tournament/index.tsx:150 #: src/features/tournaments/components/upcoming-tournament/index.tsx:150
msgid "Bracket" msgid "Bracket"
msgstr "Bracket" msgstr "Bracket"
@@ -573,6 +589,10 @@ msgstr "Bracket Preview"
msgid "Bracket Reset" msgid "Bracket Reset"
msgstr "Bracket Reset" msgstr "Bracket Reset"
#: src/themes/brutal/manifest.ts:6
msgid "Brutal"
msgstr "Brutal"
#: src/features/players/components/player-stats-table.tsx:139 #: src/features/players/components/player-stats-table.tsx:139
#: src/features/tournaments/components/tournament-stats.tsx:152 #: src/features/tournaments/components/tournament-stats.tsx:152
msgid "CA" msgid "CA"
@@ -605,12 +625,16 @@ msgstr "Cannot create teams with an odd number of players. Please have one playe
msgid "Capture current state" msgid "Capture current state"
msgstr "Capture current state" msgstr "Capture current state"
#: src/themes/cassette/manifest.ts:145
msgid "Cassette Futurism"
msgstr "Cassette Futurism"
#: src/features/players/components/player-stats-table.tsx:138 #: src/features/players/components/player-stats-table.tsx:138
#: src/features/tournaments/components/tournament-stats.tsx:144 #: src/features/tournaments/components/tournament-stats.tsx:144
msgid "CF" msgid "CF"
msgstr "CF" msgstr "CF"
#: src/features/tournaments/components/podium.tsx:56 #: src/features/tournaments/components/podium.tsx:50
msgid "Champions" msgid "Champions"
msgstr "Champions" msgstr "Champions"
@@ -623,17 +647,21 @@ msgstr "Change Logo"
msgid "Choose a player" msgid "Choose a player"
msgstr "Choose a player" msgstr "Choose a player"
#: src/themes/clay/manifest.ts:144
msgid "Claymation"
msgstr "Claymation"
#: src/features/players/components/league-head-to-head.tsx:99 #: src/features/players/components/league-head-to-head.tsx:99
msgid "Clear selected player" msgid "Clear selected player"
msgstr "Clear selected player" msgstr "Clear selected player"
#: src/components/sheet/modal.tsx:35 #: src/components/sheet/modal.tsx:35
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:165 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:169
#: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:36 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:36
msgid "Close" msgid "Close"
msgstr "Close" msgstr "Close"
#: src/components/avatar.tsx:107 #: src/components/avatar.tsx:127
msgid "Close image preview" msgid "Close image preview"
msgstr "Close image preview" msgstr "Close image preview"
@@ -785,10 +813,22 @@ msgstr "Custom Hex Code"
msgid "Dark" msgid "Dark"
msgstr "Dark" msgstr "Dark"
#: src/themes/gothic/manifest.ts:6
msgid "Dark Academia"
msgstr "Dark Academia"
#: src/features/activities/components/activities-table.tsx:356 #: src/features/activities/components/activities-table.tsx:356
msgid "Date" msgid "Date"
msgstr "Date" msgstr "Date"
#: src/themes/ocean/manifest.ts:117
msgid "Deep Sea"
msgstr "Deep Sea"
#: src/themes/default/manifest.ts:6
msgid "Default"
msgstr "Default"
#: src/features/tournaments/components/tournament-form.tsx:150 #: src/features/tournaments/components/tournament-form.tsx:150
msgid "Description" msgid "Description"
msgstr "Description" msgstr "Description"
@@ -833,16 +873,16 @@ msgstr "Edit Enrolled Teams"
msgid "Edit Enrollments" msgid "Edit Enrollments"
msgstr "Edit Enrollments" msgstr "Edit Enrollments"
#: src/features/bracket/components/match-card.tsx:388 #: src/features/bracket/components/match-card.tsx:392
msgid "Edit Match" msgid "Edit Match"
msgstr "Edit Match" msgstr "Edit Match"
#: src/features/bracket/components/match-card.tsx:376 #: src/features/bracket/components/match-card.tsx:380
#: src/features/tournaments/components/group-match-card.tsx:187 #: src/features/tournaments/components/group-match-card.tsx:187
msgid "Edit match score" msgid "Edit match score"
msgstr "Edit match score" msgstr "Edit match score"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:110 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:114
msgid "Edit Prediction" msgid "Edit Prediction"
msgstr "Edit Prediction" msgstr "Edit Prediction"
@@ -914,7 +954,7 @@ msgid "Enrolled Players"
msgstr "Enrolled Players" msgstr "Enrolled Players"
#: src/features/tournaments/components/edit-enrolled-teams.tsx:151 #: src/features/tournaments/components/edit-enrolled-teams.tsx:151
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:141 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:145
#: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:31 #: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:31
msgid "Enrolled Teams" msgid "Enrolled Teams"
msgstr "Enrolled Teams" msgstr "Enrolled Teams"
@@ -1104,6 +1144,10 @@ msgstr "Go back"
msgid "Go Back" msgid "Go Back"
msgstr "Go Back" msgstr "Go Back"
#: src/themes/seventies/manifest.ts:6
msgid "Groovy"
msgstr "Groovy"
#. placeholder {0}: group.groupName #. placeholder {0}: group.groupName
#. placeholder {0}: group.name #. placeholder {0}: group.name
#: src/features/tournaments/components/group-preview.tsx:24 #: src/features/tournaments/components/group-preview.tsx:24
@@ -1138,7 +1182,7 @@ msgstr "Group stage generated successfully!"
msgid "Group Stage Setup" msgid "Group Stage Setup"
msgstr "Group Stage Setup" msgstr "Group Stage Setup"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:94 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:98
msgid "Groups" msgid "Groups"
msgstr "Groups" msgstr "Groups"
@@ -1154,8 +1198,8 @@ msgstr "Have you played before?"
#: src/app/routes/_authed/stats.tsx:47 #: src/app/routes/_authed/stats.tsx:47
#: src/features/bracket/components/match-dock.tsx:173 #: src/features/bracket/components/match-dock.tsx:173
#: src/features/bracket/components/match-dock.tsx:215 #: src/features/bracket/components/match-dock.tsx:215
#: src/features/matches/components/match-card.tsx:83 #: src/features/matches/components/match-card.tsx:84
#: src/features/matches/components/match-card.tsx:231 #: src/features/matches/components/match-card.tsx:228
#: src/features/players/components/league-head-to-head.tsx:183 #: src/features/players/components/league-head-to-head.tsx:183
#: src/features/players/components/profile/header.tsx:90 #: src/features/players/components/profile/header.tsx:90
msgid "Head to Head" msgid "Head to Head"
@@ -1339,7 +1383,7 @@ msgstr "M"
msgid "Mainline" msgid "Mainline"
msgstr "Mainline" msgstr "Mainline"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:110 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:114
msgid "Make Prediction" msgid "Make Prediction"
msgstr "Make Prediction" msgstr "Make Prediction"
@@ -1347,7 +1391,7 @@ msgstr "Make Prediction"
msgid "Make Your Prediction" msgid "Make Your Prediction"
msgstr "Make Your Prediction" msgstr "Make Your Prediction"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:85 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:89
#: src/features/tournaments/components/upcoming-tournament/index.tsx:143 #: src/features/tournaments/components/upcoming-tournament/index.tsx:143
msgid "Manage" msgid "Manage"
msgstr "Manage" msgstr "Manage"
@@ -1392,7 +1436,7 @@ msgstr "Match {0} · Round {1} (Losers)"
#. placeholder {0}: match.order #. placeholder {0}: match.order
#. placeholder {1}: match.home!.name #. placeholder {1}: match.home!.name
#. placeholder {2}: match.away!.name #. placeholder {2}: match.away!.name
#: src/features/bracket/components/match-card.tsx:288 #: src/features/bracket/components/match-card.tsx:292
msgid "Match {0}: {1} vs {2}" msgid "Match {0}: {1} vs {2}"
msgstr "Match {0}: {1} vs {2}" msgstr "Match {0}: {1} vs {2}"
@@ -1415,7 +1459,7 @@ msgid "Match started!"
msgstr "Match started!" msgstr "Match started!"
#: src/features/players/components/player-stats-table.tsx:366 #: src/features/players/components/player-stats-table.tsx:366
#: src/features/players/components/profile/index.tsx:73 #: src/features/players/components/profile/index.tsx:78
#: src/features/players/components/profile/skeleton.tsx:70 #: src/features/players/components/profile/skeleton.tsx:70
#: src/features/teams/components/team-profile/index.tsx:44 #: src/features/teams/components/team-profile/index.tsx:44
#: src/features/teams/components/team-profile/skeleton.tsx:75 #: src/features/teams/components/team-profile/skeleton.tsx:75
@@ -1489,6 +1533,10 @@ msgstr "nerd"
msgid "Network error. Please check your connection and try again." msgid "Network error. Please check your connection and try again."
msgstr "Network error. Please check your connection and try again." msgstr "Network error. Please check your connection and try again."
#: src/themes/neumorph/manifest.ts:6
msgid "Neumorphic"
msgstr "Neumorphic"
#: src/features/players/components/players-activity-table.tsx:27 #: src/features/players/components/players-activity-table.tsx:27
msgid "Never" msgid "Never"
msgstr "Never" msgstr "Never"
@@ -1834,7 +1882,7 @@ msgstr "Prediction Scoring:"
#: src/app/routes/_authed/tournaments/$id.predictions.tsx:23 #: src/app/routes/_authed/tournaments/$id.predictions.tsx:23
#: src/features/predictions/components/prediction-leaderboard.tsx:147 #: src/features/predictions/components/prediction-leaderboard.tsx:147
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:119 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:123
msgid "Predictions" msgid "Predictions"
msgstr "Predictions" msgstr "Predictions"
@@ -1867,6 +1915,10 @@ msgstr "PTS"
msgid "Q" msgid "Q"
msgstr "Q" msgstr "Q"
#: src/themes/quest/manifest.ts:6
msgid "Quest Log"
msgstr "Quest Log"
#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:167 #: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:167
msgid "Re-roll" msgid "Re-roll"
msgstr "Re-roll" msgstr "Re-roll"
@@ -1923,11 +1975,11 @@ msgid "rofl"
msgstr "rofl" msgstr "rofl"
#. placeholder {0}: match.round + 1 #. placeholder {0}: match.round + 1
#: src/features/matches/components/match-card.tsx:76 #: src/features/matches/components/match-card.tsx:77
msgid "Round {0}" msgid "Round {0}"
msgstr "Round {0}" msgstr "Round {0}"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:138 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:142
#: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23
msgid "Rules" msgid "Rules"
msgstr "Rules" msgstr "Rules"
@@ -2099,7 +2151,7 @@ msgstr "Server Functions"
msgid "serverFn name" msgid "serverFn name"
msgstr "serverFn name" msgstr "serverFn name"
#: src/app/routes/_authed/settings.tsx:15 #: src/app/routes/_authed/settings.tsx:16
#: src/features/core/components/settings-button.tsx:19 #: src/features/core/components/settings-button.tsx:19
msgid "Settings" msgid "Settings"
msgstr "Settings" msgstr "Settings"
@@ -2118,10 +2170,18 @@ msgstr "Showing {0} of {1} players"
msgid "Shuffle Groups" msgid "Shuffle Groups"
msgstr "Shuffle Groups" msgstr "Shuffle Groups"
#: src/app/routes/_authed/settings.tsx:42 #: src/app/routes/_authed/settings.tsx:44
msgid "Sign Out" msgid "Sign Out"
msgstr "Sign Out" msgstr "Sign Out"
#: src/themes/sketch/manifest.ts:6
msgid "Sketchbook"
msgstr "Sketchbook"
#: src/themes/skeuo/manifest.ts:6
msgid "Skeuomorphic"
msgstr "Skeuomorphic"
#: src/components/DefaultCatchBoundary.tsx:89 #: src/components/DefaultCatchBoundary.tsx:89
msgid "Something went wrong" msgid "Something went wrong"
msgstr "Something went wrong" msgstr "Something went wrong"
@@ -2188,7 +2248,7 @@ msgstr "Start"
msgid "Start Date" msgid "Start Date"
msgstr "Start Date" msgstr "Start Date"
#: src/features/bracket/components/match-card.tsx:354 #: src/features/bracket/components/match-card.tsx:358
#: src/features/tournaments/components/group-match-card.tsx:168 #: src/features/tournaments/components/group-match-card.tsx:168
msgid "Start match" msgid "Start match"
msgstr "Start match" msgstr "Start match"
@@ -2316,11 +2376,11 @@ msgstr "Team updated but logo upload failed: {0}"
msgid "Team updated successfully!" msgid "Team updated successfully!"
msgstr "Team updated successfully!" msgstr "Team updated successfully!"
#: src/features/players/components/profile/index.tsx:78 #: src/features/players/components/profile/index.tsx:83
#: src/features/players/components/profile/skeleton.tsx:75 #: src/features/players/components/profile/skeleton.tsx:75
#: src/features/tournaments/components/profile/index.tsx:32 #: src/features/tournaments/components/profile/index.tsx:32
#: src/features/tournaments/components/profile/skeleton.tsx:89 #: src/features/tournaments/components/profile/skeleton.tsx:89
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:133 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:137
msgid "Teams" msgid "Teams"
msgstr "Teams" msgstr "Teams"
@@ -2328,6 +2388,10 @@ msgstr "Teams"
msgid "Teams created successfully!" msgid "Teams created successfully!"
msgstr "Teams created successfully!" msgstr "Teams created successfully!"
#: src/themes/terminal/manifest.ts:6
msgid "Terminal"
msgstr "Terminal"
#: src/app/routes/api/push/test.ts:29 #: src/app/routes/api/push/test.ts:29
msgid "Test notification — push is working on this device." msgid "Test notification — push is working on this device."
msgstr "Test notification — push is working on this device." msgstr "Test notification — push is working on this device."
@@ -2352,6 +2416,11 @@ msgstr "The other team"
msgid "The requested resource was not found" msgid "The requested resource was not found"
msgstr "The requested resource was not found" msgstr "The requested resource was not found"
#: src/features/settings/components/theme-picker.tsx:40
#: src/features/settings/components/theme-picker.tsx:68
msgid "Theme"
msgstr "Theme"
#: src/features/players/components/player-head-to-head-sheet.tsx:107 #: src/features/players/components/player-head-to-head-sheet.tsx:107
msgid "These players have not faced each other yet." msgid "These players have not faced each other yet."
msgstr "These players have not faced each other yet." msgstr "These players have not faced each other yet."
@@ -2421,7 +2490,7 @@ msgstr "Tournament created successfully!"
msgid "Tournament not found" msgid "Tournament not found"
msgstr "Tournament not found" msgstr "Tournament not found"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:148 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:152
#: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:25 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:25
msgid "Tournament Rules" msgid "Tournament Rules"
msgstr "Tournament Rules" msgstr "Tournament Rules"
@@ -2512,7 +2581,7 @@ msgstr "User Agent"
msgid "versus" msgid "versus"
msgstr "versus" msgstr "versus"
#: src/features/players/components/profile/index.tsx:61 #: src/features/players/components/profile/index.tsx:66
msgid "View all badges" msgid "View all badges"
msgstr "View all badges" msgstr "View all badges"
@@ -2529,7 +2598,7 @@ msgid "View Groups"
msgstr "View Groups" msgstr "View Groups"
#: src/features/bracket/components/match-dock.tsx:178 #: src/features/bracket/components/match-dock.tsx:178
#: src/features/matches/components/match-card.tsx:88 #: src/features/matches/components/match-card.tsx:89
msgid "View head-to-head" msgid "View head-to-head"
msgstr "View head-to-head" msgstr "View head-to-head"
@@ -2537,6 +2606,10 @@ msgstr "View head-to-head"
msgid "View Predictions" msgid "View Predictions"
msgstr "View Predictions" msgstr "View Predictions"
#: src/themes/wireframe/manifest.ts:118
msgid "Viewport"
msgstr "Viewport"
#: src/app/routes/api/spotify/playback.ts:70 #: src/app/routes/api/spotify/playback.ts:70
msgid "volumePercent must be a number" msgid "volumePercent must be a number"
msgstr "volumePercent must be a number" msgstr "volumePercent must be a number"
+106 -33
View File
@@ -13,7 +13,7 @@ msgstr ""
"Language-Team: \n" "Language-Team: \n"
"Plural-Forms: \n" "Plural-Forms: \n"
#: src/features/matches/components/match-card.tsx:77 #: src/features/matches/components/match-card.tsx:78
msgid " (Losers)" msgid " (Losers)"
msgstr " (Perdedores)" msgstr " (Perdedores)"
@@ -140,7 +140,7 @@ msgstr "{0}ms"
msgid "{count, plural, one {Players (#)} other {Players (#)}}" msgid "{count, plural, one {Players (#)} other {Players (#)}}"
msgstr "{count, plural, one {Jugadores (#)} other {Jugadores (#)}}" msgstr "{count, plural, one {Jugadores (#)} other {Jugadores (#)}}"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:130 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:134
#: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:24 #: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:24
msgid "{count, plural, one {Teams (#)} other {Teams (#)}}" msgid "{count, plural, one {Teams (#)} other {Teams (#)}}"
msgstr "{count, plural, one {Equipos (#)} other {Equipos (#)}}" msgstr "{count, plural, one {Equipos (#)} other {Equipos (#)}}"
@@ -201,7 +201,7 @@ msgstr "Eliminación directa de {size} equipos"
msgid "* Confidence penalty applied for players with <15 matches" msgid "* Confidence penalty applied for players with <15 matches"
msgstr "* Se aplica una penalización de confianza a jugadores con menos de 15 partidos" msgstr "* Se aplica una penalización de confianza a jugadores con menos de 15 partidos"
#: src/features/bracket/components/match-card.tsx:320 #: src/features/bracket/components/match-card.tsx:324
#: src/features/predictions/components/prediction-match-card.tsx:108 #: src/features/predictions/components/prediction-match-card.tsx:108
msgid "* If necessary" msgid "* If necessary"
msgstr "* Si es necesario" msgstr "* Si es necesario"
@@ -327,7 +327,7 @@ msgstr "1s"
msgid "2. Earlier submission" msgid "2. Earlier submission"
msgstr "2. Envío más temprano" msgstr "2. Envío más temprano"
#: src/features/tournaments/components/podium.tsx:66 #: src/features/tournaments/components/podium.tsx:59
msgid "2nd place" msgid "2nd place"
msgstr "2do lugar" msgstr "2do lugar"
@@ -335,7 +335,7 @@ msgstr "2do lugar"
msgid "30s" msgid "30s"
msgstr "30s" msgstr "30s"
#: src/features/tournaments/components/podium.tsx:76 #: src/features/tournaments/components/podium.tsx:68
msgid "3rd place" msgid "3rd place"
msgstr "3er lugar" msgstr "3er lugar"
@@ -406,6 +406,10 @@ msgstr "Agregar Equipo"
msgid "Admin" msgid "Admin"
msgstr "Admin" msgstr "Admin"
#: src/themes/aero/manifest.ts:117
msgid "Aero"
msgstr "Aero"
#: src/app/routes/_authed/stats.tsx:59 #: src/app/routes/_authed/stats.tsx:59
#: src/features/activities/components/activities-table.tsx:328 #: src/features/activities/components/activities-table.tsx:328
#: src/features/players/components/profile/index.tsx:28 #: src/features/players/components/profile/index.tsx:28
@@ -436,11 +440,11 @@ msgstr "Ocurrió un error inesperado al intentar enviar el código de un solo us
msgid "An unexpected error occurred. Please try again." msgid "An unexpected error occurred. Please try again."
msgstr "Ocurrió un error inesperado. Intenta de nuevo." msgstr "Ocurrió un error inesperado. Intenta de nuevo."
#: src/features/bracket/components/match-card.tsx:336 #: src/features/bracket/components/match-card.tsx:340
msgid "Announce matchup" msgid "Announce matchup"
msgstr "Anunciar enfrentamiento" msgstr "Anunciar enfrentamiento"
#: src/app/routes/_authed/settings.tsx:34 #: src/app/routes/_authed/settings.tsx:35
msgid "Appearance" msgid "Appearance"
msgstr "Apariencia" msgstr "Apariencia"
@@ -452,6 +456,10 @@ msgstr "¿Seguro que quieres darte de baja de este torneo? Puedes volver a inscr
msgid "Arguments" msgid "Arguments"
msgstr "Argumentos" msgstr "Argumentos"
#: src/themes/deco/manifest.ts:6
msgid "Art Deco"
msgstr "Art Déco"
#: src/features/tournaments/components/manage-tournament.tsx:69 #: src/features/tournaments/components/manage-tournament.tsx:69
msgid "Assign Partners" msgid "Assign Partners"
msgstr "Asignar Compañeros" msgstr "Asignar Compañeros"
@@ -554,12 +562,20 @@ msgstr "Tipo de insignia"
msgid "Badges" msgid "Badges"
msgstr "Insignias" msgstr "Insignias"
#: src/themes/bauhaus/manifest.ts:6
msgid "Bauhaus"
msgstr "Bauhaus"
#: src/themes/circus/manifest.ts:141
msgid "Big Top"
msgstr "Circo"
#: src/features/bracket/components/match-form.tsx:43 #: src/features/bracket/components/match-form.tsx:43
#: src/features/bracket/components/match-form.tsx:60 #: src/features/bracket/components/match-form.tsx:60
msgid "Both teams cannot have 10 cups" msgid "Both teams cannot have 10 cups"
msgstr "Los dos equipos no pueden tener 10 vasos" msgstr "Los dos equipos no pueden tener 10 vasos"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:102 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:106
#: src/features/tournaments/components/upcoming-tournament/index.tsx:150 #: src/features/tournaments/components/upcoming-tournament/index.tsx:150
msgid "Bracket" msgid "Bracket"
msgstr "Bracket" msgstr "Bracket"
@@ -573,6 +589,10 @@ msgstr "Vista previa del bracket"
msgid "Bracket Reset" msgid "Bracket Reset"
msgstr "Reinicio del bracket" msgstr "Reinicio del bracket"
#: src/themes/brutal/manifest.ts:6
msgid "Brutal"
msgstr "Brutal"
#: src/features/players/components/player-stats-table.tsx:139 #: src/features/players/components/player-stats-table.tsx:139
#: src/features/tournaments/components/tournament-stats.tsx:152 #: src/features/tournaments/components/tournament-stats.tsx:152
msgid "CA" msgid "CA"
@@ -605,12 +625,16 @@ msgstr "No se pueden crear equipos con un número impar de jugadores. Pide que u
msgid "Capture current state" msgid "Capture current state"
msgstr "Capturar estado actual" msgstr "Capturar estado actual"
#: src/themes/cassette/manifest.ts:145
msgid "Cassette Futurism"
msgstr "Retro-Tec"
#: src/features/players/components/player-stats-table.tsx:138 #: src/features/players/components/player-stats-table.tsx:138
#: src/features/tournaments/components/tournament-stats.tsx:144 #: src/features/tournaments/components/tournament-stats.tsx:144
msgid "CF" msgid "CF"
msgstr "VF" msgstr "VF"
#: src/features/tournaments/components/podium.tsx:56 #: src/features/tournaments/components/podium.tsx:50
msgid "Champions" msgid "Champions"
msgstr "Campeones" msgstr "Campeones"
@@ -623,17 +647,21 @@ msgstr "Cambiar logo"
msgid "Choose a player" msgid "Choose a player"
msgstr "Elige un jugador" msgstr "Elige un jugador"
#: src/themes/clay/manifest.ts:144
msgid "Claymation"
msgstr "Plastilina"
#: src/features/players/components/league-head-to-head.tsx:99 #: src/features/players/components/league-head-to-head.tsx:99
msgid "Clear selected player" msgid "Clear selected player"
msgstr "Quitar jugador seleccionado" msgstr "Quitar jugador seleccionado"
#: src/components/sheet/modal.tsx:35 #: src/components/sheet/modal.tsx:35
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:165 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:169
#: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:36 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:36
msgid "Close" msgid "Close"
msgstr "Cerrar" msgstr "Cerrar"
#: src/components/avatar.tsx:107 #: src/components/avatar.tsx:127
msgid "Close image preview" msgid "Close image preview"
msgstr "Cerrar vista previa de la imagen" msgstr "Cerrar vista previa de la imagen"
@@ -785,10 +813,22 @@ msgstr "Código hex personalizado"
msgid "Dark" msgid "Dark"
msgstr "Oscuro" msgstr "Oscuro"
#: src/themes/gothic/manifest.ts:6
msgid "Dark Academia"
msgstr "Academia Oscura"
#: src/features/activities/components/activities-table.tsx:356 #: src/features/activities/components/activities-table.tsx:356
msgid "Date" msgid "Date"
msgstr "Fecha" msgstr "Fecha"
#: src/themes/ocean/manifest.ts:117
msgid "Deep Sea"
msgstr "Mar Profundo"
#: src/themes/default/manifest.ts:6
msgid "Default"
msgstr "Predeterminado"
#: src/features/tournaments/components/tournament-form.tsx:150 #: src/features/tournaments/components/tournament-form.tsx:150
msgid "Description" msgid "Description"
msgstr "Descripción" msgstr "Descripción"
@@ -833,16 +873,16 @@ msgstr "Editar equipos inscritos"
msgid "Edit Enrollments" msgid "Edit Enrollments"
msgstr "Editar inscripciones" msgstr "Editar inscripciones"
#: src/features/bracket/components/match-card.tsx:388 #: src/features/bracket/components/match-card.tsx:392
msgid "Edit Match" msgid "Edit Match"
msgstr "Editar partido" msgstr "Editar partido"
#: src/features/bracket/components/match-card.tsx:376 #: src/features/bracket/components/match-card.tsx:380
#: src/features/tournaments/components/group-match-card.tsx:187 #: src/features/tournaments/components/group-match-card.tsx:187
msgid "Edit match score" msgid "Edit match score"
msgstr "Editar marcador del partido" msgstr "Editar marcador del partido"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:110 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:114
msgid "Edit Prediction" msgid "Edit Prediction"
msgstr "Editar predicción" msgstr "Editar predicción"
@@ -914,7 +954,7 @@ msgid "Enrolled Players"
msgstr "Jugadores inscritos" msgstr "Jugadores inscritos"
#: src/features/tournaments/components/edit-enrolled-teams.tsx:151 #: src/features/tournaments/components/edit-enrolled-teams.tsx:151
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:141 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:145
#: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:31 #: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:31
msgid "Enrolled Teams" msgid "Enrolled Teams"
msgstr "Equipos inscritos" msgstr "Equipos inscritos"
@@ -1104,6 +1144,10 @@ msgstr "Regresar"
msgid "Go Back" msgid "Go Back"
msgstr "Regresar" msgstr "Regresar"
#: src/themes/seventies/manifest.ts:6
msgid "Groovy"
msgstr "Setentero"
#. placeholder {0}: group.groupName #. placeholder {0}: group.groupName
#. placeholder {0}: group.name #. placeholder {0}: group.name
#: src/features/tournaments/components/group-preview.tsx:24 #: src/features/tournaments/components/group-preview.tsx:24
@@ -1138,7 +1182,7 @@ msgstr "¡Fase de grupos generada con éxito!"
msgid "Group Stage Setup" msgid "Group Stage Setup"
msgstr "Configuración de la fase de grupos" msgstr "Configuración de la fase de grupos"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:94 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:98
msgid "Groups" msgid "Groups"
msgstr "Grupos" msgstr "Grupos"
@@ -1154,8 +1198,8 @@ msgstr "¿Ya has jugado antes?"
#: src/app/routes/_authed/stats.tsx:47 #: src/app/routes/_authed/stats.tsx:47
#: src/features/bracket/components/match-dock.tsx:173 #: src/features/bracket/components/match-dock.tsx:173
#: src/features/bracket/components/match-dock.tsx:215 #: src/features/bracket/components/match-dock.tsx:215
#: src/features/matches/components/match-card.tsx:83 #: src/features/matches/components/match-card.tsx:84
#: src/features/matches/components/match-card.tsx:231 #: src/features/matches/components/match-card.tsx:228
#: src/features/players/components/league-head-to-head.tsx:183 #: src/features/players/components/league-head-to-head.tsx:183
#: src/features/players/components/profile/header.tsx:90 #: src/features/players/components/profile/header.tsx:90
msgid "Head to Head" msgid "Head to Head"
@@ -1339,7 +1383,7 @@ msgstr "J"
msgid "Mainline" msgid "Mainline"
msgstr "Mainline" msgstr "Mainline"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:110 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:114
msgid "Make Prediction" msgid "Make Prediction"
msgstr "Hacer predicción" msgstr "Hacer predicción"
@@ -1347,7 +1391,7 @@ msgstr "Hacer predicción"
msgid "Make Your Prediction" msgid "Make Your Prediction"
msgstr "Haz tu predicción" msgstr "Haz tu predicción"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:85 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:89
#: src/features/tournaments/components/upcoming-tournament/index.tsx:143 #: src/features/tournaments/components/upcoming-tournament/index.tsx:143
msgid "Manage" msgid "Manage"
msgstr "Administrar" msgstr "Administrar"
@@ -1392,7 +1436,7 @@ msgstr "Partido {0} · Ronda {1} (Perdedores)"
#. placeholder {0}: match.order #. placeholder {0}: match.order
#. placeholder {1}: match.home!.name #. placeholder {1}: match.home!.name
#. placeholder {2}: match.away!.name #. placeholder {2}: match.away!.name
#: src/features/bracket/components/match-card.tsx:288 #: src/features/bracket/components/match-card.tsx:292
msgid "Match {0}: {1} vs {2}" msgid "Match {0}: {1} vs {2}"
msgstr "Partido {0}: {1} vs {2}" msgstr "Partido {0}: {1} vs {2}"
@@ -1415,7 +1459,7 @@ msgid "Match started!"
msgstr "¡Empezó el partido!" msgstr "¡Empezó el partido!"
#: src/features/players/components/player-stats-table.tsx:366 #: src/features/players/components/player-stats-table.tsx:366
#: src/features/players/components/profile/index.tsx:73 #: src/features/players/components/profile/index.tsx:78
#: src/features/players/components/profile/skeleton.tsx:70 #: src/features/players/components/profile/skeleton.tsx:70
#: src/features/teams/components/team-profile/index.tsx:44 #: src/features/teams/components/team-profile/index.tsx:44
#: src/features/teams/components/team-profile/skeleton.tsx:75 #: src/features/teams/components/team-profile/skeleton.tsx:75
@@ -1489,6 +1533,10 @@ msgstr "nerd"
msgid "Network error. Please check your connection and try again." msgid "Network error. Please check your connection and try again."
msgstr "Error de red. Revisa tu conexión e intenta de nuevo." msgstr "Error de red. Revisa tu conexión e intenta de nuevo."
#: src/themes/neumorph/manifest.ts:6
msgid "Neumorphic"
msgstr "Neumórfico"
#: src/features/players/components/players-activity-table.tsx:27 #: src/features/players/components/players-activity-table.tsx:27
msgid "Never" msgid "Never"
msgstr "Nunca" msgstr "Nunca"
@@ -1834,7 +1882,7 @@ msgstr "Puntuación de predicciones:"
#: src/app/routes/_authed/tournaments/$id.predictions.tsx:23 #: src/app/routes/_authed/tournaments/$id.predictions.tsx:23
#: src/features/predictions/components/prediction-leaderboard.tsx:147 #: src/features/predictions/components/prediction-leaderboard.tsx:147
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:119 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:123
msgid "Predictions" msgid "Predictions"
msgstr "Predicciones" msgstr "Predicciones"
@@ -1867,6 +1915,10 @@ msgstr "PTS"
msgid "Q" msgid "Q"
msgstr "C" msgstr "C"
#: src/themes/quest/manifest.ts:6
msgid "Quest Log"
msgstr "Misiones"
#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:167 #: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:167
msgid "Re-roll" msgid "Re-roll"
msgstr "Volver a sortear" msgstr "Volver a sortear"
@@ -1923,11 +1975,11 @@ msgid "rofl"
msgstr "carcajada" msgstr "carcajada"
#. placeholder {0}: match.round + 1 #. placeholder {0}: match.round + 1
#: src/features/matches/components/match-card.tsx:76 #: src/features/matches/components/match-card.tsx:77
msgid "Round {0}" msgid "Round {0}"
msgstr "Ronda {0}" msgstr "Ronda {0}"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:138 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:142
#: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23
msgid "Rules" msgid "Rules"
msgstr "Reglas" msgstr "Reglas"
@@ -2099,7 +2151,7 @@ msgstr "Funciones del servidor"
msgid "serverFn name" msgid "serverFn name"
msgstr "nombre de serverFn" msgstr "nombre de serverFn"
#: src/app/routes/_authed/settings.tsx:15 #: src/app/routes/_authed/settings.tsx:16
#: src/features/core/components/settings-button.tsx:19 #: src/features/core/components/settings-button.tsx:19
msgid "Settings" msgid "Settings"
msgstr "Configuración" msgstr "Configuración"
@@ -2118,10 +2170,18 @@ msgstr "Mostrando {0} de {1} jugadores"
msgid "Shuffle Groups" msgid "Shuffle Groups"
msgstr "Mezclar grupos" msgstr "Mezclar grupos"
#: src/app/routes/_authed/settings.tsx:42 #: src/app/routes/_authed/settings.tsx:44
msgid "Sign Out" msgid "Sign Out"
msgstr "Cerrar sesión" msgstr "Cerrar sesión"
#: src/themes/sketch/manifest.ts:6
msgid "Sketchbook"
msgstr "Cuaderno"
#: src/themes/skeuo/manifest.ts:6
msgid "Skeuomorphic"
msgstr "Esqueuomórfico"
#: src/components/DefaultCatchBoundary.tsx:89 #: src/components/DefaultCatchBoundary.tsx:89
msgid "Something went wrong" msgid "Something went wrong"
msgstr "Algo salió mal" msgstr "Algo salió mal"
@@ -2188,7 +2248,7 @@ msgstr "Inicio"
msgid "Start Date" msgid "Start Date"
msgstr "Fecha de inicio" msgstr "Fecha de inicio"
#: src/features/bracket/components/match-card.tsx:354 #: src/features/bracket/components/match-card.tsx:358
#: src/features/tournaments/components/group-match-card.tsx:168 #: src/features/tournaments/components/group-match-card.tsx:168
msgid "Start match" msgid "Start match"
msgstr "Iniciar partido" msgstr "Iniciar partido"
@@ -2316,11 +2376,11 @@ msgstr "Equipo actualizado, pero falló la carga del logo: {0}"
msgid "Team updated successfully!" msgid "Team updated successfully!"
msgstr "¡Equipo actualizado exitosamente!" msgstr "¡Equipo actualizado exitosamente!"
#: src/features/players/components/profile/index.tsx:78 #: src/features/players/components/profile/index.tsx:83
#: src/features/players/components/profile/skeleton.tsx:75 #: src/features/players/components/profile/skeleton.tsx:75
#: src/features/tournaments/components/profile/index.tsx:32 #: src/features/tournaments/components/profile/index.tsx:32
#: src/features/tournaments/components/profile/skeleton.tsx:89 #: src/features/tournaments/components/profile/skeleton.tsx:89
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:133 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:137
msgid "Teams" msgid "Teams"
msgstr "Equipos" msgstr "Equipos"
@@ -2328,6 +2388,10 @@ msgstr "Equipos"
msgid "Teams created successfully!" msgid "Teams created successfully!"
msgstr "¡Equipos creados exitosamente!" msgstr "¡Equipos creados exitosamente!"
#: src/themes/terminal/manifest.ts:6
msgid "Terminal"
msgstr "Terminal"
#: src/app/routes/api/push/test.ts:29 #: src/app/routes/api/push/test.ts:29
msgid "Test notification — push is working on this device." msgid "Test notification — push is working on this device."
msgstr "Notificación de prueba: las notificaciones push funcionan en este dispositivo." msgstr "Notificación de prueba: las notificaciones push funcionan en este dispositivo."
@@ -2352,6 +2416,11 @@ msgstr "El otro equipo"
msgid "The requested resource was not found" msgid "The requested resource was not found"
msgstr "No se encontró el recurso solicitado" msgstr "No se encontró el recurso solicitado"
#: src/features/settings/components/theme-picker.tsx:40
#: src/features/settings/components/theme-picker.tsx:68
msgid "Theme"
msgstr "Tema"
#: src/features/players/components/player-head-to-head-sheet.tsx:107 #: src/features/players/components/player-head-to-head-sheet.tsx:107
msgid "These players have not faced each other yet." msgid "These players have not faced each other yet."
msgstr "Estos jugadores aún no se han enfrentado." msgstr "Estos jugadores aún no se han enfrentado."
@@ -2421,7 +2490,7 @@ msgstr "¡Torneo creado exitosamente!"
msgid "Tournament not found" msgid "Tournament not found"
msgstr "Torneo no encontrado" msgstr "Torneo no encontrado"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:148 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:152
#: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:25 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:25
msgid "Tournament Rules" msgid "Tournament Rules"
msgstr "Reglas del torneo" msgstr "Reglas del torneo"
@@ -2512,7 +2581,7 @@ msgstr "User Agent"
msgid "versus" msgid "versus"
msgstr "contra" msgstr "contra"
#: src/features/players/components/profile/index.tsx:61 #: src/features/players/components/profile/index.tsx:66
msgid "View all badges" msgid "View all badges"
msgstr "Ver todas las insignias" msgstr "Ver todas las insignias"
@@ -2529,7 +2598,7 @@ msgid "View Groups"
msgstr "Ver grupos" msgstr "Ver grupos"
#: src/features/bracket/components/match-dock.tsx:178 #: src/features/bracket/components/match-dock.tsx:178
#: src/features/matches/components/match-card.tsx:88 #: src/features/matches/components/match-card.tsx:89
msgid "View head-to-head" msgid "View head-to-head"
msgstr "Ver cara a cara" msgstr "Ver cara a cara"
@@ -2537,6 +2606,10 @@ msgstr "Ver cara a cara"
msgid "View Predictions" msgid "View Predictions"
msgstr "Ver predicciones" msgstr "Ver predicciones"
#: src/themes/wireframe/manifest.ts:118
msgid "Viewport"
msgstr "Viewport"
#: src/app/routes/api/spotify/playback.ts:70 #: src/app/routes/api/spotify/playback.ts:70
msgid "volumePercent must be a number" msgid "volumePercent must be a number"
msgstr "volumePercent debe ser un número" msgstr "volumePercent debe ser un número"
+106 -33
View File
@@ -13,7 +13,7 @@ msgstr ""
"Language-Team: \n" "Language-Team: \n"
"Plural-Forms: \n" "Plural-Forms: \n"
#: src/features/matches/components/match-card.tsx:77 #: src/features/matches/components/match-card.tsx:78
msgid " (Losers)" msgid " (Losers)"
msgstr " (敗者側)" msgstr " (敗者側)"
@@ -140,7 +140,7 @@ msgstr "{0}ms"
msgid "{count, plural, one {Players (#)} other {Players (#)}}" msgid "{count, plural, one {Players (#)} other {Players (#)}}"
msgstr "{count, plural, one {プレイヤー(#)} other {プレイヤー(#)}}" msgstr "{count, plural, one {プレイヤー(#)} other {プレイヤー(#)}}"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:130 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:134
#: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:24 #: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:24
msgid "{count, plural, one {Teams (#)} other {Teams (#)}}" msgid "{count, plural, one {Teams (#)} other {Teams (#)}}"
msgstr "{count, plural, one {チーム(#)} other {チーム(#)}}" msgstr "{count, plural, one {チーム(#)} other {チーム(#)}}"
@@ -201,7 +201,7 @@ msgstr "{size}チームノックアウト"
msgid "* Confidence penalty applied for players with <15 matches" msgid "* Confidence penalty applied for players with <15 matches"
msgstr "※試合数が15未満のプレイヤーには信頼度ペナルティが適用されます" msgstr "※試合数が15未満のプレイヤーには信頼度ペナルティが適用されます"
#: src/features/bracket/components/match-card.tsx:320 #: src/features/bracket/components/match-card.tsx:324
#: src/features/predictions/components/prediction-match-card.tsx:108 #: src/features/predictions/components/prediction-match-card.tsx:108
msgid "* If necessary" msgid "* If necessary"
msgstr "※必要な場合" msgstr "※必要な場合"
@@ -327,7 +327,7 @@ msgstr "1秒"
msgid "2. Earlier submission" msgid "2. Earlier submission"
msgstr "2. 早い提出" msgstr "2. 早い提出"
#: src/features/tournaments/components/podium.tsx:66 #: src/features/tournaments/components/podium.tsx:59
msgid "2nd place" msgid "2nd place"
msgstr "2位" msgstr "2位"
@@ -335,7 +335,7 @@ msgstr "2位"
msgid "30s" msgid "30s"
msgstr "30秒" msgstr "30秒"
#: src/features/tournaments/components/podium.tsx:76 #: src/features/tournaments/components/podium.tsx:68
msgid "3rd place" msgid "3rd place"
msgstr "3位" msgstr "3位"
@@ -406,6 +406,10 @@ msgstr "チームを追加"
msgid "Admin" msgid "Admin"
msgstr "管理者" msgstr "管理者"
#: src/themes/aero/manifest.ts:117
msgid "Aero"
msgstr "エアロ"
#: src/app/routes/_authed/stats.tsx:59 #: src/app/routes/_authed/stats.tsx:59
#: src/features/activities/components/activities-table.tsx:328 #: src/features/activities/components/activities-table.tsx:328
#: src/features/players/components/profile/index.tsx:28 #: src/features/players/components/profile/index.tsx:28
@@ -436,11 +440,11 @@ msgstr "ワンタイムパスコードの送信中に予期しないエラーが
msgid "An unexpected error occurred. Please try again." msgid "An unexpected error occurred. Please try again."
msgstr "予期しないエラーが発生しました。もう一度お試しください。" msgstr "予期しないエラーが発生しました。もう一度お試しください。"
#: src/features/bracket/components/match-card.tsx:336 #: src/features/bracket/components/match-card.tsx:340
msgid "Announce matchup" msgid "Announce matchup"
msgstr "対戦カードを発表" msgstr "対戦カードを発表"
#: src/app/routes/_authed/settings.tsx:34 #: src/app/routes/_authed/settings.tsx:35
msgid "Appearance" msgid "Appearance"
msgstr "外観" msgstr "外観"
@@ -452,6 +456,10 @@ msgstr "このトーナメントから登録を解除してもよろしいです
msgid "Arguments" msgid "Arguments"
msgstr "引数" msgstr "引数"
#: src/themes/deco/manifest.ts:6
msgid "Art Deco"
msgstr "アールデコ"
#: src/features/tournaments/components/manage-tournament.tsx:69 #: src/features/tournaments/components/manage-tournament.tsx:69
msgid "Assign Partners" msgid "Assign Partners"
msgstr "パートナーを割り当て" msgstr "パートナーを割り当て"
@@ -554,12 +562,20 @@ msgstr "バッジの種類"
msgid "Badges" msgid "Badges"
msgstr "バッジ" msgstr "バッジ"
#: src/themes/bauhaus/manifest.ts:6
msgid "Bauhaus"
msgstr "バウハウス"
#: src/themes/circus/manifest.ts:141
msgid "Big Top"
msgstr "サーカス"
#: src/features/bracket/components/match-form.tsx:43 #: src/features/bracket/components/match-form.tsx:43
#: src/features/bracket/components/match-form.tsx:60 #: src/features/bracket/components/match-form.tsx:60
msgid "Both teams cannot have 10 cups" msgid "Both teams cannot have 10 cups"
msgstr "両チーム同時に10カップにはなりません" msgstr "両チーム同時に10カップにはなりません"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:102 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:106
#: src/features/tournaments/components/upcoming-tournament/index.tsx:150 #: src/features/tournaments/components/upcoming-tournament/index.tsx:150
msgid "Bracket" msgid "Bracket"
msgstr "ブラケット" msgstr "ブラケット"
@@ -573,6 +589,10 @@ msgstr "ブラケットプレビュー"
msgid "Bracket Reset" msgid "Bracket Reset"
msgstr "ブラケットリセット" msgstr "ブラケットリセット"
#: src/themes/brutal/manifest.ts:6
msgid "Brutal"
msgstr "ブルータル"
#: src/features/players/components/player-stats-table.tsx:139 #: src/features/players/components/player-stats-table.tsx:139
#: src/features/tournaments/components/tournament-stats.tsx:152 #: src/features/tournaments/components/tournament-stats.tsx:152
msgid "CA" msgid "CA"
@@ -605,12 +625,16 @@ msgstr "参加者が奇数のためチームを作成できません。どなた
msgid "Capture current state" msgid "Capture current state"
msgstr "現在の状態をキャプチャ" msgstr "現在の状態をキャプチャ"
#: src/themes/cassette/manifest.ts:145
msgid "Cassette Futurism"
msgstr "レトロフューチャー"
#: src/features/players/components/player-stats-table.tsx:138 #: src/features/players/components/player-stats-table.tsx:138
#: src/features/tournaments/components/tournament-stats.tsx:144 #: src/features/tournaments/components/tournament-stats.tsx:144
msgid "CF" msgid "CF"
msgstr "CF" msgstr "CF"
#: src/features/tournaments/components/podium.tsx:56 #: src/features/tournaments/components/podium.tsx:50
msgid "Champions" msgid "Champions"
msgstr "チャンピオン" msgstr "チャンピオン"
@@ -623,17 +647,21 @@ msgstr "ロゴを変更"
msgid "Choose a player" msgid "Choose a player"
msgstr "プレイヤーを選択" msgstr "プレイヤーを選択"
#: src/themes/clay/manifest.ts:144
msgid "Claymation"
msgstr "クレイ"
#: src/features/players/components/league-head-to-head.tsx:99 #: src/features/players/components/league-head-to-head.tsx:99
msgid "Clear selected player" msgid "Clear selected player"
msgstr "選択したプレイヤーを解除" msgstr "選択したプレイヤーを解除"
#: src/components/sheet/modal.tsx:35 #: src/components/sheet/modal.tsx:35
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:165 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:169
#: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:36 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:36
msgid "Close" msgid "Close"
msgstr "閉じる" msgstr "閉じる"
#: src/components/avatar.tsx:107 #: src/components/avatar.tsx:127
msgid "Close image preview" msgid "Close image preview"
msgstr "画像プレビューを閉じる" msgstr "画像プレビューを閉じる"
@@ -785,10 +813,22 @@ msgstr "カスタムHEXコード"
msgid "Dark" msgid "Dark"
msgstr "ダーク" msgstr "ダーク"
#: src/themes/gothic/manifest.ts:6
msgid "Dark Academia"
msgstr "ダークアカデミア"
#: src/features/activities/components/activities-table.tsx:356 #: src/features/activities/components/activities-table.tsx:356
msgid "Date" msgid "Date"
msgstr "日付" msgstr "日付"
#: src/themes/ocean/manifest.ts:117
msgid "Deep Sea"
msgstr "深海"
#: src/themes/default/manifest.ts:6
msgid "Default"
msgstr "デフォルト"
#: src/features/tournaments/components/tournament-form.tsx:150 #: src/features/tournaments/components/tournament-form.tsx:150
msgid "Description" msgid "Description"
msgstr "説明" msgstr "説明"
@@ -833,16 +873,16 @@ msgstr "参加チームを編集"
msgid "Edit Enrollments" msgid "Edit Enrollments"
msgstr "参加登録を編集" msgstr "参加登録を編集"
#: src/features/bracket/components/match-card.tsx:388 #: src/features/bracket/components/match-card.tsx:392
msgid "Edit Match" msgid "Edit Match"
msgstr "試合を編集" msgstr "試合を編集"
#: src/features/bracket/components/match-card.tsx:376 #: src/features/bracket/components/match-card.tsx:380
#: src/features/tournaments/components/group-match-card.tsx:187 #: src/features/tournaments/components/group-match-card.tsx:187
msgid "Edit match score" msgid "Edit match score"
msgstr "試合スコアを編集" msgstr "試合スコアを編集"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:110 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:114
msgid "Edit Prediction" msgid "Edit Prediction"
msgstr "予想を編集" msgstr "予想を編集"
@@ -914,7 +954,7 @@ msgid "Enrolled Players"
msgstr "登録済みプレイヤー" msgstr "登録済みプレイヤー"
#: src/features/tournaments/components/edit-enrolled-teams.tsx:151 #: src/features/tournaments/components/edit-enrolled-teams.tsx:151
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:141 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:145
#: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:31 #: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:31
msgid "Enrolled Teams" msgid "Enrolled Teams"
msgstr "登録済みチーム" msgstr "登録済みチーム"
@@ -1104,6 +1144,10 @@ msgstr "戻る"
msgid "Go Back" msgid "Go Back"
msgstr "戻る" msgstr "戻る"
#: src/themes/seventies/manifest.ts:6
msgid "Groovy"
msgstr "グルーヴィー"
#. placeholder {0}: group.groupName #. placeholder {0}: group.groupName
#. placeholder {0}: group.name #. placeholder {0}: group.name
#: src/features/tournaments/components/group-preview.tsx:24 #: src/features/tournaments/components/group-preview.tsx:24
@@ -1138,7 +1182,7 @@ msgstr "グループステージを生成しました!"
msgid "Group Stage Setup" msgid "Group Stage Setup"
msgstr "グループステージ設定" msgstr "グループステージ設定"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:94 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:98
msgid "Groups" msgid "Groups"
msgstr "グループ" msgstr "グループ"
@@ -1154,8 +1198,8 @@ msgstr "参加したことはありますか?"
#: src/app/routes/_authed/stats.tsx:47 #: src/app/routes/_authed/stats.tsx:47
#: src/features/bracket/components/match-dock.tsx:173 #: src/features/bracket/components/match-dock.tsx:173
#: src/features/bracket/components/match-dock.tsx:215 #: src/features/bracket/components/match-dock.tsx:215
#: src/features/matches/components/match-card.tsx:83 #: src/features/matches/components/match-card.tsx:84
#: src/features/matches/components/match-card.tsx:231 #: src/features/matches/components/match-card.tsx:228
#: src/features/players/components/league-head-to-head.tsx:183 #: src/features/players/components/league-head-to-head.tsx:183
#: src/features/players/components/profile/header.tsx:90 #: src/features/players/components/profile/header.tsx:90
msgid "Head to Head" msgid "Head to Head"
@@ -1339,7 +1383,7 @@ msgstr "M"
msgid "Mainline" msgid "Mainline"
msgstr "メインライン" msgstr "メインライン"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:110 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:114
msgid "Make Prediction" msgid "Make Prediction"
msgstr "予想する" msgstr "予想する"
@@ -1347,7 +1391,7 @@ msgstr "予想する"
msgid "Make Your Prediction" msgid "Make Your Prediction"
msgstr "予想を入力" msgstr "予想を入力"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:85 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:89
#: src/features/tournaments/components/upcoming-tournament/index.tsx:143 #: src/features/tournaments/components/upcoming-tournament/index.tsx:143
msgid "Manage" msgid "Manage"
msgstr "管理" msgstr "管理"
@@ -1392,7 +1436,7 @@ msgstr "試合{0}・ラウンド{1}(敗者側)"
#. placeholder {0}: match.order #. placeholder {0}: match.order
#. placeholder {1}: match.home!.name #. placeholder {1}: match.home!.name
#. placeholder {2}: match.away!.name #. placeholder {2}: match.away!.name
#: src/features/bracket/components/match-card.tsx:288 #: src/features/bracket/components/match-card.tsx:292
msgid "Match {0}: {1} vs {2}" msgid "Match {0}: {1} vs {2}"
msgstr "試合{0}: {1} vs {2}" msgstr "試合{0}: {1} vs {2}"
@@ -1415,7 +1459,7 @@ msgid "Match started!"
msgstr "試合開始!" msgstr "試合開始!"
#: src/features/players/components/player-stats-table.tsx:366 #: src/features/players/components/player-stats-table.tsx:366
#: src/features/players/components/profile/index.tsx:73 #: src/features/players/components/profile/index.tsx:78
#: src/features/players/components/profile/skeleton.tsx:70 #: src/features/players/components/profile/skeleton.tsx:70
#: src/features/teams/components/team-profile/index.tsx:44 #: src/features/teams/components/team-profile/index.tsx:44
#: src/features/teams/components/team-profile/skeleton.tsx:75 #: src/features/teams/components/team-profile/skeleton.tsx:75
@@ -1489,6 +1533,10 @@ msgstr "オタク"
msgid "Network error. Please check your connection and try again." msgid "Network error. Please check your connection and try again."
msgstr "ネットワークエラーです。接続を確認してもう一度お試しください。" msgstr "ネットワークエラーです。接続を確認してもう一度お試しください。"
#: src/themes/neumorph/manifest.ts:6
msgid "Neumorphic"
msgstr "ネウモーフィズム"
#: src/features/players/components/players-activity-table.tsx:27 #: src/features/players/components/players-activity-table.tsx:27
msgid "Never" msgid "Never"
msgstr "なし" msgstr "なし"
@@ -1834,7 +1882,7 @@ msgstr "予想の得点方式:"
#: src/app/routes/_authed/tournaments/$id.predictions.tsx:23 #: src/app/routes/_authed/tournaments/$id.predictions.tsx:23
#: src/features/predictions/components/prediction-leaderboard.tsx:147 #: src/features/predictions/components/prediction-leaderboard.tsx:147
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:119 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:123
msgid "Predictions" msgid "Predictions"
msgstr "予想" msgstr "予想"
@@ -1867,6 +1915,10 @@ msgstr "PTS"
msgid "Q" msgid "Q"
msgstr "Q" msgstr "Q"
#: src/themes/quest/manifest.ts:6
msgid "Quest Log"
msgstr "クエスト"
#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:167 #: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:167
msgid "Re-roll" msgid "Re-roll"
msgstr "再抽選" msgstr "再抽選"
@@ -1923,11 +1975,11 @@ msgid "rofl"
msgstr "大笑い" msgstr "大笑い"
#. placeholder {0}: match.round + 1 #. placeholder {0}: match.round + 1
#: src/features/matches/components/match-card.tsx:76 #: src/features/matches/components/match-card.tsx:77
msgid "Round {0}" msgid "Round {0}"
msgstr "ラウンド{0}" msgstr "ラウンド{0}"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:138 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:142
#: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23
msgid "Rules" msgid "Rules"
msgstr "ルール" msgstr "ルール"
@@ -2099,7 +2151,7 @@ msgstr "サーバー関数"
msgid "serverFn name" msgid "serverFn name"
msgstr "serverFn名" msgstr "serverFn名"
#: src/app/routes/_authed/settings.tsx:15 #: src/app/routes/_authed/settings.tsx:16
#: src/features/core/components/settings-button.tsx:19 #: src/features/core/components/settings-button.tsx:19
msgid "Settings" msgid "Settings"
msgstr "設定" msgstr "設定"
@@ -2118,10 +2170,18 @@ msgstr "{1}人中{0}人のプレイヤーを表示"
msgid "Shuffle Groups" msgid "Shuffle Groups"
msgstr "グループをシャッフル" msgstr "グループをシャッフル"
#: src/app/routes/_authed/settings.tsx:42 #: src/app/routes/_authed/settings.tsx:44
msgid "Sign Out" msgid "Sign Out"
msgstr "サインアウト" msgstr "サインアウト"
#: src/themes/sketch/manifest.ts:6
msgid "Sketchbook"
msgstr "スケッチ"
#: src/themes/skeuo/manifest.ts:6
msgid "Skeuomorphic"
msgstr "スキューモーフィズム"
#: src/components/DefaultCatchBoundary.tsx:89 #: src/components/DefaultCatchBoundary.tsx:89
msgid "Something went wrong" msgid "Something went wrong"
msgstr "問題が発生しました" msgstr "問題が発生しました"
@@ -2188,7 +2248,7 @@ msgstr "開始"
msgid "Start Date" msgid "Start Date"
msgstr "開始日" msgstr "開始日"
#: src/features/bracket/components/match-card.tsx:354 #: src/features/bracket/components/match-card.tsx:358
#: src/features/tournaments/components/group-match-card.tsx:168 #: src/features/tournaments/components/group-match-card.tsx:168
msgid "Start match" msgid "Start match"
msgstr "試合を開始" msgstr "試合を開始"
@@ -2316,11 +2376,11 @@ msgstr "チームを更新しましたが、ロゴのアップロードに失敗
msgid "Team updated successfully!" msgid "Team updated successfully!"
msgstr "チームを更新しました!" msgstr "チームを更新しました!"
#: src/features/players/components/profile/index.tsx:78 #: src/features/players/components/profile/index.tsx:83
#: src/features/players/components/profile/skeleton.tsx:75 #: src/features/players/components/profile/skeleton.tsx:75
#: src/features/tournaments/components/profile/index.tsx:32 #: src/features/tournaments/components/profile/index.tsx:32
#: src/features/tournaments/components/profile/skeleton.tsx:89 #: src/features/tournaments/components/profile/skeleton.tsx:89
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:133 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:137
msgid "Teams" msgid "Teams"
msgstr "チーム" msgstr "チーム"
@@ -2328,6 +2388,10 @@ msgstr "チーム"
msgid "Teams created successfully!" msgid "Teams created successfully!"
msgstr "チームを作成しました!" msgstr "チームを作成しました!"
#: src/themes/terminal/manifest.ts:6
msgid "Terminal"
msgstr "ターミナル"
#: src/app/routes/api/push/test.ts:29 #: src/app/routes/api/push/test.ts:29
msgid "Test notification — push is working on this device." msgid "Test notification — push is working on this device."
msgstr "テスト通知 — このデバイスでプッシュ通知が機能しています。" msgstr "テスト通知 — このデバイスでプッシュ通知が機能しています。"
@@ -2352,6 +2416,11 @@ msgstr "相手チーム"
msgid "The requested resource was not found" msgid "The requested resource was not found"
msgstr "リクエストされたリソースが見つかりませんでした" msgstr "リクエストされたリソースが見つかりませんでした"
#: src/features/settings/components/theme-picker.tsx:40
#: src/features/settings/components/theme-picker.tsx:68
msgid "Theme"
msgstr "テーマ"
#: src/features/players/components/player-head-to-head-sheet.tsx:107 #: src/features/players/components/player-head-to-head-sheet.tsx:107
msgid "These players have not faced each other yet." msgid "These players have not faced each other yet."
msgstr "このプレイヤー同士はまだ対戦していません。" msgstr "このプレイヤー同士はまだ対戦していません。"
@@ -2421,7 +2490,7 @@ msgstr "トーナメントを作成しました!"
msgid "Tournament not found" msgid "Tournament not found"
msgstr "トーナメントが見つかりません" msgstr "トーナメントが見つかりません"
#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:148 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:152
#: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:25 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:25
msgid "Tournament Rules" msgid "Tournament Rules"
msgstr "トーナメントルール" msgstr "トーナメントルール"
@@ -2512,7 +2581,7 @@ msgstr "ユーザーエージェント"
msgid "versus" msgid "versus"
msgstr "対" msgstr "対"
#: src/features/players/components/profile/index.tsx:61 #: src/features/players/components/profile/index.tsx:66
msgid "View all badges" msgid "View all badges"
msgstr "すべてのバッジを見る" msgstr "すべてのバッジを見る"
@@ -2529,7 +2598,7 @@ msgid "View Groups"
msgstr "グループを見る" msgstr "グループを見る"
#: src/features/bracket/components/match-dock.tsx:178 #: src/features/bracket/components/match-dock.tsx:178
#: src/features/matches/components/match-card.tsx:88 #: src/features/matches/components/match-card.tsx:89
msgid "View head-to-head" msgid "View head-to-head"
msgstr "対戦成績を見る" msgstr "対戦成績を見る"
@@ -2537,6 +2606,10 @@ msgstr "対戦成績を見る"
msgid "View Predictions" msgid "View Predictions"
msgstr "予想を見る" msgstr "予想を見る"
#: src/themes/wireframe/manifest.ts:118
msgid "Viewport"
msgstr "ビューポート"
#: src/app/routes/api/spotify/playback.ts:70 #: src/app/routes/api/spotify/playback.ts:70
msgid "volumePercent must be a number" msgid "volumePercent must be a number"
msgstr "volumePercentは数値である必要があります" msgstr "volumePercentは数値である必要があります"
+280
View File
@@ -0,0 +1,280 @@
# FLXN theme system
A theme is a self-contained folder under `src/themes/<id>/` containing exactly
two files. Themes are auto-discovered via `import.meta.glob` — adding a theme
is adding a folder, removing one is deleting it. No shared file is ever edited.
```
src/themes/
<id>/
manifest.ts # metadata + optional JS-side Mantine override
theme.css # all visual work, scoped under [data-theme="<id>"]
```
Activation: the provider sets `data-theme="<id>"` on `<html>` (mirrored to
localStorage `flxn-theme` for the pre-hydration script in `__root.tsx`).
The preference is stored per-user in SuperTokens metadata (`metadata.theme`).
## manifest.ts
```ts
import { msg } from "@lingui/core/macro";
import type { ThemeManifest } from "../types";
const manifest: ThemeManifest = {
id: "comic", // MUST equal the folder name
label: msg`Comic`, // i18n — always msg``
colorSchemes: "both", // or "light" / "dark" to force a scheme
fontLinks: [ // injected only while theme is active;
"https://fonts.googleapis.com/css2?family=Bangers&display=swap",
], // browsers fetch font files lazily
chromeColors: { // browser-chrome theme-color meta
light: { base: "#fde047", dimmed: "#ca8a04" },
dark: { base: "#1c1917", dimmed: "#0c0a09" },
},
mantine: { // optional Mantine override, merged onto
defaultRadius: "xs", // the base theme via mergeThemeOverrides.
components: { /* defaultProps, vars, … */ },
},
};
export default manifest;
```
Rules:
- `label` must use `msg` from `@lingui/core/macro` (never raw strings, never
global `t`). Translations are handled centrally afterwards.
- Prefer doing visual work in `theme.css`; use `mantine` only for things CSS
cannot express (defaultProps, per-component `vars` resolvers).
## Accent color (REQUIRED)
The user picks an accent (blue, red, green, yellow, grape, orange, pink,
lime) and EVERY theme must honor it — there is no way to lock the accent.
- Primary/interactive styling must derive from the primary aliases
(`--mantine-primary-color-filled`, `-light`, `-6`, …) or from the accent
ramps generically — never from one hardcoded brand hue.
- A theme MAY retune all eight accent ramps (`--mantine-color-blue-*`,
`--mantine-color-red-*`, …) to fit its world — e.g. neon versions, pastel
versions, phosphor-tinted versions — as long as the eight choices remain
visually distinct. Retuning shades is encouraged; ignoring the choice is
forbidden.
- Theme-flavor decoration may still use fixed hues (a comic's halftone tint,
a blueprint's grid ink), but anything that reads as "the accent" —
filled buttons, active tabs/nav, focus rings, selection — follows the user.
## light-dark() trap
Never use `light-dark()` in a declaration whose selector matches `<html>`
(any `[data-theme=…]` token block): postcss-preset-mantine rewrites it into
a descendant selector that can never match the root element, so the dark
branch silently dies. Use the explicit compound scheme selectors (shape 2)
instead. Inside descendant rules (`[data-theme] .mantine-Button-root`)
light-dark() is fine.
## theme.css — selector templates (specificity is load-bearing)
Mantine emits its variables from a runtime `<style>` tag that appears AFTER
our bundled CSS, so theme overrides must WIN ON SPECIFICITY, not order. Use
these exact selector shapes:
```css
/* 1. Tokens for any scheme — (0,2,0), beats Mantine's :root block */
[data-theme="comic"][data-theme] {
--mantine-radius-default: 0;
--mantine-font-family: "Comic Neue", cursive;
}
/* 2. Per-scheme tokens — (0,3,0), beats Mantine's :root[scheme] blocks */
[data-theme="comic"][data-theme][data-mantine-color-scheme="light"] {
--mantine-color-body: #fef9c3;
}
[data-theme="comic"][data-theme][data-mantine-color-scheme="dark"] {
--mantine-color-body: #1c1917;
}
/* 3. Component restyles — descendant selectors on static classes */
[data-theme="comic"] .mantine-Button-root {
border: 3px solid #000;
box-shadow: 4px 4px 0 #000;
}
/* 4. Full-page decorations (scanlines, halftone, grain) */
[data-theme="comic"] body::before {
content: "";
position: fixed;
inset: 0;
z-index: 9998; /* above content, below --mantine-z-index-max */
pointer-events: none;
/* embed textures as data-URI SVG/gradients — no external requests */
}
```
The doubled `[data-theme]` attribute is intentional (specificity bump).
Still never use `html[data-theme]` or `:root[data-theme]` — the doubled
attribute form matches `<html>` at higher specificity and stays portable.
## Previews (no per-theme work required)
The settings picker renders each theme in its own sandboxed same-origin
srcdoc iframe (`theme-preview.tsx`): the iframe's `<html>` carries the
theme's `data-theme`/`data-mantine-color-scheme`, its head gets a snapshot
of the app's real stylesheets, the theme's `fontLinks`, and the theme's
Mantine JS-side variables computed with Mantine's own resolver from the
same `baseTheme` + `manifest.mantine` + accent merge the live provider
performs. Previews are therefore 1:1 with the active theme by construction
— fonts, textures, html/body-level rules and all — and nothing bleeds
between the active theme and a preview. There is no preview variable
contract to maintain, and `--mantine-primary-color-*` must still never be
pinned by a theme (the user's accent must show through everywhere,
previews included).
## What you can override (cheat sheet)
Mantine components style themselves from CSS variables — overriding variables
retints the whole app with zero React work.
- Palettes: `--mantine-color-<name>-<0..9>` and per-color variant vars
(`-filled`, `-filled-hover`, `-light`, `-light-hover`, `-light-color`,
`-outline`, `-outline-hover`, `-text`); primary aliases
`--mantine-primary-color-{0..9,filled,filled-hover,light,light-hover,light-color,contrast}`.
- Scheme-dependent semantics (define per scheme, selector 2):
`--mantine-color-body`, `-text`, `-dimmed`, `-bright`, `-anchor`, `-error`,
`-placeholder`, `-default`, `-default-hover`, `-default-color`,
`-default-border`.
- Shape: `--mantine-radius-{xs..xl}`, `--mantine-radius-default`.
- Elevation: `--mantine-shadow-{xs..xl}` (composite strings — neobrutalism
uses `4px 4px 0 #000`, glass uses soft large blurs).
- Type: `--mantine-font-family{,-monospace,-headings}`,
`--mantine-font-size-{xs..xl}`, `--mantine-h{1..6}-font-size/-line-height/-font-weight`,
`--mantine-heading-font-weight`, `--mantine-line-height{,-xs..xl}`.
- Spacing: `--mantine-spacing-{xs..xl}` (be conservative — layout shifts).
- App tokens (see `base.css`): `--flxn-gold/-silver/-bronze`,
`--flxn-toast-success/-error`, `--flxn-podium-{1,2,3}-{bg,border,label}`,
`--flxn-showcase-bg`, `--flxn-overlay`, `--flxn-dotgrid-color`,
`--flxn-pop-shadow`. Override these with selector 1/2 shapes (they beat
base.css's `:root` on specificity).
Static classes for restyling (most-used components in this app): Button,
ActionIcon, Paper, Card, TextInput/Input (`.mantine-Input-input`), Select,
Badge, Tabs (`-list`, `-tab`), SegmentedControl (`-root`, `-indicator`,
`-label`), Modal (`-content`, `-header`), Avatar, ThemeIcon, Skeleton,
Loader, Alert, Tooltip, Popover (`-dropdown`), Switch, Checkbox, Progress,
PinInput, NavLink, Notification, AppShell (`-header`, `-navbar`, `-main`),
Divider, Indicator — as `.mantine-<Component>-<part>`. State comes as data
attributes: `[data-variant]`, `[data-active]`, `[data-disabled]`, etc.
Custom app components (sheet drawers, nav tiles, list rows, carousels) are
built on the same `--mantine-*`/`--flxn-*` variables and follow automatically.
## App component hooks
Stable classes/tokens for custom components (defaults live in
`src/themes/hooks.css`; themes override under `[data-theme]` scopes — theme
files load after hooks.css, so equal-specificity rules win by order):
- `.flxn-tabs` / `.flxn-tab` (+`[data-active]`) / `.flxn-tabs-indicator` —
the swipeable tab strip used on profile/tournament pages. Restyle these so
tabs belong to the theme (block active states, underlines, boxed tabs…).
TWO STATES: resting (mid-page) must stay FLAT — transparent background,
no strip chrome, blending into the page; the docked treatment (background,
rules, shadows, textures) goes on `.flxn-tabs[data-stuck]` only, which the
component sets while the strip is pinned to the top. hooks.css provides
the transition between states. Tab/indicator styling must stay legible
over the bare page background in the resting state.
STACKING: the indicator is Mantine FloatingIndicator — absolutely
positioned, so it would paint OVER the static tab labels; hooks.css
lifts `.flxn-tab` (`position: relative; z-index: 1`) so labels always
render on top. Never z-index the indicator above 1 or an opaque fill
swallows the labels.
PAIRING RULE: an accent-filled indicator pill/block (or accent-filled
`.flxn-tab[data-active]`) MUST pair its label with `--flxn-on-accent`
(black/white computed from the actual cascaded fill — safe under CSS
ramp retunes, unlike `--mantine-primary-color-contrast`, which Mantine
computes from the JS theme colors and can disagree with a CSS-retuned
fill). Never accent text over an accent fill. If the indicator is an
underline/rule, accent text is fine.
NO glow/soft shadows on `.flxn-tabs-indicator` (owner preference): a
clean underline or flat plate only. Rich active-tab chrome (wells,
engraving, plates beyond the indicator) belongs under
`.flxn-tabs[data-stuck]` like the strip chrome — resting tabs stay flat.
- `.flxn-tile` / `.flxn-tile-icon` — home/tournament nav grid tiles. Treat
like Card/Paper: give them the theme's surface language.
- (retired) `.flxn-see-all` — "see all" actions are plain Mantine subtle
Buttons now, accent-driven by design. Do NOT reintroduce per-theme chip
styling for them; they inherit only your generic Button treatment.
- `.flxn-bracket-card` — the small dense match cards inside the bracket
view (they are Mantine Cards). Exclude them from heavy Card decorations
(valances, ornaments) that suit full-width cards but overwhelm bracket
density.
- `.flxn-match-card` + `.flxn-reaction-row` — a match card with a reaction
tray tucked underneath (tray = full border + full radius, hidden top edge).
Keep the pair reading as one construction in your theme: matching borders
and backgrounds, tray background contrasting subtly with the card.
- `.flxn-badge-tile` — badge grid tile motion hook; tile shape follows
`--flxn-badge-radius` (default 12px).
- `.flxn-avatar-frame` + `--flxn-avatar-radius` (default `50%`) +
`--flxn-avatar-border` (default `1px solid` default-border) — avatars.
The frame owns the avatar's SINGLE border via the token; NEVER put a
border/outline on `.mantine-Avatar-root` (hooks.css force-strips it —
doing so doubles every ring). The frame is a plain Box, so generic
Paper restyles don't reach it; square-language themes set the radius
to 0–4px and restyle via the two tokens (plus `.flxn-avatar-frame` for
anything richer, e.g. shadows/bezels).
- `.flxn-tile-icon` — keep icons FLAT: tint the glyph (color), no
background plates/borders/padding behind it. A plate is only justified
when the theme's whole language demands it (rare); default to flat.
## Hard rules
- EVERYTHING scoped under `[data-theme="<id>"]`. Zero unscoped selectors —
a theme must be inert when inactive.
- Support both schemes unless the design genuinely demands one; then set
`colorSchemes: "light" | "dark"` (the engine forces it).
- Accessibility gates:
- Wrap flicker/pulse/marquee animations in
`@media (prefers-reduced-motion: no-preference) { … }`.
- Give `backdrop-filter` surfaces a solid fallback via
`@supports not (backdrop-filter: blur(1px))` and respect
`@media (prefers-reduced-transparency: reduce)`.
- Keep real borders on interactive elements (forced-colors mode strips
shadows/gradients).
- Body text must stay ≥ 4.5:1 contrast in every supported scheme.
- Mantine hardcodes WHITE on `[data-combobox-selected]` (Select options)
and `theme.autoContrast` does not reach it. hooks.css fixes this
globally with `--flxn-on-accent`; if your theme overrides the
selected-option styling, pair the fill with an explicit legible
on-color yourself.
- Any `background-clip: text` gradient text needs a
`@media (forced-colors: active)` fallback (`background-image: none;
color: CanvasText`) or headings vanish in forced-colors mode.
- Re-flavored `.flxn-press` transforms MUST be wrapped in
`@media (prefers-reduced-motion: no-preference)` — theme selectors
outrank the global reduce kill in the provider.
- Placeholder text ≥3:1 and dimmed text ≥4.5:1 against the body color.
- Text must never share its hue with the fill directly behind it
(accent-on-accent, gold-on-gold…). Accent fills pair with
`--flxn-on-accent` (base.css token: black/white picked from the actual
cascaded `--mantine-primary-color-filled`); accent washes pair with
`--mantine-primary-color-light-color`; accent text needs a neutral
backing. Applies to tab indicators, SegmentedControl, active NavLinks,
badges, chips — anywhere a label sits on a colored plate.
- Performance gates:
- Never animate variables on `:root`/`html` — animate leaf elements.
- At most 1–2 full-viewport overlay pseudo-elements, `pointer-events: none`.
- Limit `backdrop-filter` to a handful of surfaces; never animate blur.
- Textures/assets as data-URIs inside theme.css; no network requests.
- Don't break layout: the app is a mobile-first PWA (AppShell header +
navbar). Radius/borders/shadows/fonts are fair game; avoid changing
spacing scales drastically or adding outer margins to shell parts.
- The press feedback (`.flxn-press`, scale on :active) is global; themes may
re-flavor it, e.g. `[data-theme="brutal"] .flxn-press:active { transform:
translate(4px, 4px); }`.
- i18n: manifest strings via `msg` only. Do NOT run lingui extract/compile —
handled centrally.
## Verifying a theme
`bun x tsc --noEmit` must pass. Do not run the dev server; visual QA happens
centrally after all themes land.
+137
View File
@@ -0,0 +1,137 @@
import { msg } from "@lingui/core/macro";
import type { ThemeManifest } from "../types";
/* Candy-gloss Aero ramps for all eight accents. Declared in JS (not
theme.css) so Mantine regenerates every derived variant var (-light rgba,
-outline, autoContrast picks) from the retuned shades, whichever accent
the user selects. */
const aeroColors = {
// aqua
blue: [
"#e7f6fd",
"#d0edfb",
"#a6ddf6",
"#79ccf0",
"#55bdec",
"#3db1e8",
"#2a9ed7",
"#1f88bd",
"#196f9b",
"#135877",
],
// glossy grass
green: [
"#eefaea",
"#d9f2d2",
"#bce8b1",
"#9cdc8d",
"#7fd06d",
"#66c353",
"#4fb23c",
"#3f9630",
"#337b27",
"#286120",
],
// chartreuse jelly
lime: [
"#f6fbe4",
"#ebf6c6",
"#dcefa0",
"#cbe677",
"#bbdc52",
"#abd135",
"#96bd20",
"#7da019",
"#648213",
"#4e660f",
],
// candy apple
red: [
"#fdeef0",
"#fbd9dd",
"#f7b7bf",
"#f2929e",
"#ec7280",
"#e65567",
"#db3a50",
"#bd2c41",
"#9b2335",
"#791b2a",
],
// glossy sunbeam
yellow: [
"#fff9e1",
"#fff0bd",
"#ffe492",
"#fcd668",
"#f7c845",
"#f0b929",
"#e3a614",
"#c28a0e",
"#9e6f0a",
"#7c5607",
],
// jelly purple
grape: [
"#f7f0fd",
"#ecdcfa",
"#dbc0f5",
"#c79fee",
"#b581e6",
"#a468dd",
"#8f4dcd",
"#7a3cb3",
"#643093",
"#4e2573",
],
// goldfish amber
orange: [
"#fff3e4",
"#ffe4c4",
"#ffcf9b",
"#fdb56d",
"#f89c45",
"#f18827",
"#e37113",
"#c25c0d",
"#9e4a0a",
"#7b3a08",
],
// bubblegum
pink: [
"#fdeff5",
"#fbdae9",
"#f8b9d6",
"#f394bf",
"#ec72a9",
"#e55596",
"#d63c82",
"#b82e6d",
"#962558",
"#751d45",
],
} as const;
const manifest: ThemeManifest = {
id: "aero",
label: msg`Aero`,
colorSchemes: "both",
fontLinks: [
"https://fonts.googleapis.com/css2?family=Nunito+Sans:opsz,wght@6..12,400;6..12,600;6..12,700;6..12,800&display=swap",
],
chromeColors: {
light: { base: "#d3edfb", dimmed: "#8fcdee" },
dark: { base: "#0a2536", dimmed: "#051925" },
},
mantine: {
// Juicy mid-tone fills in both schemes; bright candy fills (yellow,
// lime) and mid-luminance aqua get dark text instead of low-contrast
// white (aqua blue-6 luminance ≈ 0.30 must land above the threshold).
primaryShade: { light: 6, dark: 7 },
autoContrast: true,
luminanceThreshold: 0.28,
colors: aeroColors,
},
};
export default manifest;
+607
View File
@@ -0,0 +1,607 @@
/* Frutiger Aero: glossy skeuomorphic surfaces over a fixed sky-to-white
gradient with static bubbles. The eight accent ramps are retuned in
manifest.ts (candy-gloss versions) and everything interactive derives
from --mantine-primary-color-* so the user's accent drives fills, active
states and focus rings; the sky/grass scenery stays fixed decoration.
Gloss is vertical half-highlight gradients (never animated) that layer
over any base color; backdrop-filter is limited to the AppShell header,
navbar and the docked ([data-stuck]) flxn tab strip. */
/* ---------------------------------------------------------------- */
/* 1. Tokens for any scheme */
/* ---------------------------------------------------------------- */
[data-theme="aero"][data-theme] {
--mantine-font-family: "Nunito Sans", "Segoe UI", "Helvetica Neue", sans-serif;
--mantine-font-family-headings: "Nunito Sans", "Segoe UI", "Helvetica Neue", sans-serif;
--mantine-heading-font-weight: 800;
/* bubbly radii */
--mantine-radius-xs: 0.5rem;
--mantine-radius-sm: 0.75rem;
--mantine-radius-md: 1rem;
--mantine-radius-lg: 1.25rem;
--mantine-radius-xl: 1.75rem;
--mantine-radius-default: 1rem;
/* glossy bezelled circles for avatars, rounded candy badge tiles */
--flxn-avatar-radius: 50%;
--flxn-avatar-border: 1px solid var(--aero-panel-border);
--flxn-badge-radius: 16px;
}
/* ---------------------------------------------------------------- */
/* 2. Per-scheme tokens */
/* ---------------------------------------------------------------- */
[data-theme="aero"][data-theme][data-mantine-color-scheme="light"] {
--mantine-color-body: #e9f6fe;
--mantine-color-text: #123c56;
--mantine-color-bright: #082738;
--mantine-color-dimmed: #4a758f;
--mantine-color-placeholder: #5f7f95;
--mantine-color-default: rgba(255, 255, 255, 0.75);
--mantine-color-default-hover: rgba(255, 255, 255, 0.92);
--mantine-color-default-color: #123c56;
--mantine-color-default-border: rgba(125, 178, 212, 0.55);
/* soft blue-tinted elevation */
--mantine-shadow-xs: 0 1px 3px rgba(23, 113, 159, 0.14);
--mantine-shadow-sm: 0 2px 8px rgba(23, 113, 159, 0.16);
--mantine-shadow-md: 0 6px 18px rgba(23, 113, 159, 0.18);
--mantine-shadow-lg: 0 10px 28px rgba(23, 113, 159, 0.2);
--mantine-shadow-xl: 0 16px 44px rgba(23, 113, 159, 0.24);
/* gloss ingredients */
--aero-panel-bg: rgba(255, 255, 255, 0.66);
--aero-panel-border: rgba(255, 255, 255, 0.75);
--aero-panel-sheen: linear-gradient(
rgba(255, 255, 255, 0.7),
rgba(255, 255, 255, 0.15) 50%,
rgba(255, 255, 255, 0) 50%
);
--aero-gloss: linear-gradient(
rgba(255, 255, 255, 0.55),
rgba(255, 255, 255, 0.14) 50%,
rgba(255, 255, 255, 0) 50%
);
--aero-gloss-soft: linear-gradient(
rgba(255, 255, 255, 0.35),
rgba(255, 255, 255, 0.08) 50%,
rgba(255, 255, 255, 0) 50%
);
--aero-solid: #f4fafe;
--aero-chrome-bg: rgba(255, 255, 255, 0.62);
--aero-input-bg: rgba(255, 255, 255, 0.85);
--aero-input-inset: inset 0 2px 4px rgba(23, 113, 159, 0.16),
inset 0 -1px 0 rgba(255, 255, 255, 0.8);
--aero-track: rgba(178, 216, 238, 0.45);
--aero-tube-inset: inset 0 2px 4px rgba(23, 113, 159, 0.22),
inset 0 -1px 0 rgba(255, 255, 255, 0.6);
--aero-indicator-bg: rgba(255, 255, 255, 0.95);
--aero-thumb: linear-gradient(#ffffff, #e6f2fa);
/* app tokens */
--flxn-gold: #f0ac12;
--flxn-silver: #90a9ba;
--flxn-bronze: #d0803f;
--flxn-toast-success: #5cb455;
--flxn-toast-error: #e56a62;
--flxn-showcase-bg: rgba(255, 255, 255, 0.72);
--flxn-overlay: rgba(16, 66, 102, 0.4);
--flxn-dotgrid-color: rgba(61, 177, 232, 0.35);
--flxn-pop-shadow: rgba(23, 113, 159, 0.3);
/* podium as glassy tiers */
--flxn-podium-1-bg: rgba(255, 232, 155, 0.6);
--flxn-podium-1-border: rgba(222, 178, 66, 0.65);
--flxn-podium-1-label: #8a6410;
--flxn-podium-2-bg: rgba(255, 255, 255, 0.6);
--flxn-podium-2-border: rgba(151, 183, 203, 0.6);
--flxn-podium-2-label: #4d6b7e;
--flxn-podium-3-bg: rgba(255, 214, 178, 0.55);
--flxn-podium-3-border: rgba(216, 138, 79, 0.6);
--flxn-podium-3-label: #92511f;
}
[data-theme="aero"][data-theme][data-mantine-color-scheme="dark"] {
--mantine-color-body: #071e2d;
--mantine-color-text: #d8edf8;
--mantine-color-bright: #f2fbff;
--mantine-color-dimmed: #8db3c8;
--mantine-color-placeholder: #618ba3;
--mantine-color-default: rgba(19, 58, 82, 0.72);
--mantine-color-default-hover: rgba(27, 74, 102, 0.85);
--mantine-color-default-color: #d8edf8;
--mantine-color-default-border: rgba(94, 193, 238, 0.28);
/* glow-edged elevation */
--mantine-shadow-xs: 0 0 0 1px rgba(94, 193, 238, 0.1), 0 2px 6px rgba(0, 10, 18, 0.5);
--mantine-shadow-sm: 0 0 0 1px rgba(94, 193, 238, 0.12), 0 4px 14px rgba(0, 10, 18, 0.55);
--mantine-shadow-md: 0 0 0 1px rgba(94, 193, 238, 0.14), 0 8px 24px rgba(0, 10, 18, 0.6);
--mantine-shadow-lg: 0 0 0 1px rgba(94, 193, 238, 0.16), 0 12px 34px rgba(0, 10, 18, 0.65);
--mantine-shadow-xl: 0 0 0 1px rgba(94, 193, 238, 0.18), 0 18px 48px rgba(0, 10, 18, 0.7);
/* deep-aqua glass ingredients */
--aero-panel-bg: rgba(12, 42, 62, 0.72);
--aero-panel-border: rgba(94, 193, 238, 0.3);
--aero-panel-sheen: linear-gradient(
rgba(151, 214, 246, 0.16),
rgba(151, 214, 246, 0.04) 50%,
rgba(151, 214, 246, 0) 50%
);
--aero-gloss: linear-gradient(
rgba(255, 255, 255, 0.28),
rgba(255, 255, 255, 0.07) 50%,
rgba(255, 255, 255, 0) 50%
);
--aero-gloss-soft: linear-gradient(
rgba(255, 255, 255, 0.14),
rgba(255, 255, 255, 0.04) 50%,
rgba(255, 255, 255, 0) 50%
);
--aero-solid: #0d2a3e;
--aero-chrome-bg: rgba(7, 28, 42, 0.68);
--aero-input-bg: rgba(6, 26, 40, 0.8);
--aero-input-inset: inset 0 2px 5px rgba(0, 10, 18, 0.6),
inset 0 -1px 0 rgba(94, 193, 238, 0.12);
--aero-track: rgba(9, 36, 54, 0.8);
--aero-tube-inset: inset 0 2px 5px rgba(0, 10, 18, 0.55),
inset 0 -1px 0 rgba(94, 193, 238, 0.1);
--aero-indicator-bg: rgba(31, 100, 140, 0.8);
--aero-thumb: linear-gradient(#e8f6fe, #a9d6ee);
/* app tokens */
--flxn-gold: #ffd76a;
--flxn-silver: #a5bccb;
--flxn-bronze: #e29a63;
--flxn-toast-success: #8fd98a;
--flxn-toast-error: #f2938c;
--flxn-showcase-bg: rgba(12, 42, 62, 0.75);
--flxn-overlay: rgba(2, 12, 20, 0.6);
--flxn-dotgrid-color: rgba(94, 193, 238, 0.22);
--flxn-pop-shadow: rgba(0, 0, 0, 0.55);
/* podium as glassy tiers */
--flxn-podium-1-bg: rgba(255, 205, 92, 0.14);
--flxn-podium-1-border: rgba(255, 205, 92, 0.4);
--flxn-podium-1-label: #ffd76a;
--flxn-podium-2-bg: rgba(190, 220, 238, 0.1);
--flxn-podium-2-border: rgba(190, 220, 238, 0.3);
--flxn-podium-2-label: #b8cfdd;
--flxn-podium-3-bg: rgba(226, 154, 99, 0.13);
--flxn-podium-3-border: rgba(226, 154, 99, 0.38);
--flxn-podium-3-label: #eba76f;
}
/* ---------------------------------------------------------------- */
/* 3. Fixed sky backdrop with static bubbles (below all content) */
/* ---------------------------------------------------------------- */
[data-theme="aero"] body::before {
content: "";
position: fixed;
inset: 0;
z-index: -1;
pointer-events: none;
}
[data-theme="aero"][data-mantine-color-scheme="light"] body::before {
background-color: #e9f6fe;
background-image:
radial-gradient(circle 0.6rem at 13.5% 20%, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0) 70%),
radial-gradient(circle 4.5rem at 15% 22%, rgba(255, 255, 255, 0) 60%, rgba(255, 255, 255, 0.5) 66%, rgba(255, 255, 255, 0.08) 72%, rgba(255, 255, 255, 0) 74%),
radial-gradient(circle 2.5rem at 79% 12%, rgba(255, 255, 255, 0) 58%, rgba(255, 255, 255, 0.45) 66%, rgba(255, 255, 255, 0) 74%),
radial-gradient(circle 6rem at 88% 64%, rgba(255, 255, 255, 0) 62%, rgba(255, 255, 255, 0.35) 68%, rgba(255, 255, 255, 0) 75%),
radial-gradient(circle 3rem at 8% 72%, rgba(255, 255, 255, 0) 58%, rgba(255, 255, 255, 0.3) 66%, rgba(255, 255, 255, 0) 74%),
radial-gradient(56rem 20rem at 50% 106%, rgba(124, 197, 118, 0.28), rgba(124, 197, 118, 0) 70%),
linear-gradient(180deg, #a5daf6 0%, #c9e9fa 32%, #e9f6fe 60%, #ffffff 100%);
}
[data-theme="aero"][data-mantine-color-scheme="dark"] body::before {
background-color: #071e2d;
background-image:
radial-gradient(circle 4rem at 16% 24%, rgba(120, 205, 245, 0) 60%, rgba(120, 205, 245, 0.2) 67%, rgba(120, 205, 245, 0) 74%),
radial-gradient(circle 2.5rem at 80% 16%, rgba(120, 205, 245, 0) 58%, rgba(120, 205, 245, 0.16) 66%, rgba(120, 205, 245, 0) 74%),
radial-gradient(40rem 28rem at 82% -4%, rgba(31, 136, 189, 0.35), transparent 65%),
radial-gradient(36rem 26rem at 10% 86%, rgba(23, 128, 118, 0.3), transparent 65%),
linear-gradient(180deg, #0c3350 0%, #0a2a40 45%, #051520 100%);
}
/* ---------------------------------------------------------------- */
/* 4. Panels: translucent white/deep-aqua with a top-half sheen */
/* ---------------------------------------------------------------- */
[data-theme="aero"] .mantine-Paper-root,
[data-theme="aero"] .mantine-Card-root,
[data-theme="aero"] .mantine-Popover-dropdown {
background-color: var(--aero-panel-bg);
background-image: var(--aero-panel-sheen);
border: 1px solid var(--aero-panel-border);
box-shadow: var(--mantine-shadow-md);
}
[data-theme="aero"] .mantine-Modal-content {
background-color: var(--aero-panel-bg);
background-image: var(--aero-panel-sheen);
border: 1px solid var(--aero-panel-border);
box-shadow: var(--mantine-shadow-xl);
}
[data-theme="aero"] .mantine-Modal-header {
background-color: transparent;
}
[data-theme="aero"] .mantine-Popover-dropdown {
box-shadow: var(--mantine-shadow-lg);
}
/* Notifications float over arbitrary content: keep near-opaque. */
[data-theme="aero"] .mantine-Notification-root {
background-color: var(--aero-solid);
background-image: var(--aero-panel-sheen);
border: 1px solid var(--aero-panel-border);
border-radius: var(--mantine-radius-lg);
box-shadow: var(--mantine-shadow-lg);
}
/* ---------------------------------------------------------------- */
/* 5. AppShell chrome — the ONLY backdrop-filter surfaces */
/* ---------------------------------------------------------------- */
[data-theme="aero"] .mantine-AppShell-header,
[data-theme="aero"] .mantine-AppShell-navbar {
background-color: var(--aero-chrome-bg);
background-image: var(--aero-gloss-soft);
-webkit-backdrop-filter: blur(14px) saturate(150%);
backdrop-filter: blur(14px) saturate(150%);
}
[data-theme="aero"] .mantine-AppShell-header {
border-bottom: 1px solid var(--aero-panel-border);
box-shadow: var(--mantine-shadow-sm);
}
[data-theme="aero"] .mantine-AppShell-navbar {
border-inline-end: 1px solid var(--aero-panel-border);
}
/* ---------------------------------------------------------------- */
/* 6. Glossy pill buttons with specular highlight */
/* ---------------------------------------------------------------- */
[data-theme="aero"] .mantine-Button-root {
position: relative;
overflow: hidden;
border-radius: 999px;
font-weight: 700;
}
[data-theme="aero"] .mantine-Button-root:is(
[data-variant="filled"],
[data-variant="default"],
[data-variant="light"],
[data-variant="gradient"]
) {
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45), var(--mantine-shadow-xs);
}
[data-theme="aero"] .mantine-Button-root[data-variant="default"] {
border: 1px solid var(--mantine-color-default-border);
}
[data-theme="aero"] .mantine-Button-root:is(
[data-variant="filled"],
[data-variant="default"],
[data-variant="light"],
[data-variant="gradient"]
)::before {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
background: var(--aero-gloss);
pointer-events: none;
}
[data-theme="aero"] .mantine-Button-inner {
position: relative;
z-index: 1;
}
[data-theme="aero"] .mantine-ActionIcon-root {
position: relative;
overflow: hidden;
border-radius: 999px;
}
[data-theme="aero"] .mantine-ActionIcon-root:is(
[data-variant="filled"],
[data-variant="default"],
[data-variant="light"],
[data-variant="gradient"]
) {
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45), var(--mantine-shadow-xs);
}
[data-theme="aero"] .mantine-ActionIcon-root:is(
[data-variant="filled"],
[data-variant="default"],
[data-variant="light"],
[data-variant="gradient"]
)::before {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
background: var(--aero-gloss);
pointer-events: none;
}
[data-theme="aero"] .mantine-ThemeIcon-root {
background-image: var(--aero-gloss);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4);
}
/* Press feedback: quick specular dim on top of the global scale. */
[data-theme="aero"] .flxn-press:active {
filter: brightness(0.95) saturate(1.05);
}
/* ---------------------------------------------------------------- */
/* 7. Glossy inset inputs */
/* ---------------------------------------------------------------- */
[data-theme="aero"] .mantine-Input-input {
background-color: var(--aero-input-bg);
border: 1px solid var(--mantine-color-default-border);
border-radius: var(--mantine-radius-lg);
box-shadow: var(--aero-input-inset);
}
[data-theme="aero"] .mantine-Input-input:focus,
[data-theme="aero"] .mantine-Input-input:focus-within {
border-color: var(--mantine-primary-color-filled);
box-shadow: var(--aero-input-inset),
0 0 0 3px color-mix(in srgb, var(--mantine-primary-color-filled) 25%, transparent);
}
[data-theme="aero"] .mantine-Checkbox-input {
border-radius: 0.55rem;
box-shadow: var(--aero-input-inset);
}
/* ---------------------------------------------------------------- */
/* 8. Glass-tube progress, tracks, toggles */
/* ---------------------------------------------------------------- */
[data-theme="aero"] .mantine-Progress-root {
background-color: var(--aero-track);
border: 1px solid var(--mantine-color-default-border);
border-radius: 999px;
box-shadow: var(--aero-tube-inset);
}
/* Shine only — background-color stays the per-instance section color. */
[data-theme="aero"] .mantine-Progress-section {
border-radius: 999px;
background-image: linear-gradient(
rgba(255, 255, 255, 0.5),
rgba(255, 255, 255, 0.14) 48%,
rgba(0, 0, 0, 0.05) 52%,
rgba(255, 255, 255, 0.08)
);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2);
}
[data-theme="aero"] .mantine-SegmentedControl-root {
background-color: var(--aero-track);
border: 1px solid var(--mantine-color-default-border);
border-radius: 999px;
box-shadow: var(--aero-tube-inset);
}
[data-theme="aero"] .mantine-SegmentedControl-indicator {
background-color: var(--aero-indicator-bg);
background-image: var(--aero-gloss);
border: 1px solid var(--aero-panel-border);
border-radius: 999px;
box-shadow: var(--mantine-shadow-sm);
}
[data-theme="aero"] .mantine-SegmentedControl-label {
font-weight: 700;
}
[data-theme="aero"] .mantine-Switch-track {
background-image: var(--aero-gloss-soft);
box-shadow: inset 0 1px 2px rgba(0, 20, 35, 0.2);
}
[data-theme="aero"] .mantine-Switch-thumb {
background: var(--aero-thumb);
border-color: transparent;
box-shadow: 0 1px 2px rgba(0, 20, 35, 0.3), inset 0 -1px 0 rgba(140, 185, 215, 0.4);
}
/* ---------------------------------------------------------------- */
/* 9. Smaller glossy details */
/* ---------------------------------------------------------------- */
[data-theme="aero"] .mantine-Badge-root {
background-image: var(--aero-gloss-soft);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35);
}
[data-theme="aero"] .mantine-Badge-root[data-variant="default"] {
background-color: var(--mantine-color-default);
border: 1px solid var(--mantine-color-default-border);
}
[data-theme="aero"] .mantine-Alert-root {
background-image: var(--aero-gloss-soft);
border-radius: var(--mantine-radius-lg);
}
[data-theme="aero"] .mantine-Tabs-list {
--tabs-list-border-color: var(--mantine-color-default-border);
}
[data-theme="aero"] .mantine-Tabs-tab {
font-weight: 700;
border-radius: var(--mantine-radius-md) var(--mantine-radius-md) 0 0;
}
[data-theme="aero"] .mantine-Tabs-tab:hover,
[data-theme="aero"] .mantine-NavLink-root:hover {
background-color: var(--mantine-color-default-hover);
}
[data-theme="aero"] .mantine-NavLink-root {
border-radius: 999px;
}
[data-theme="aero"] .mantine-Tooltip-tooltip {
background-color: rgba(10, 45, 66, 0.92);
color: #eaf7ff;
border-radius: var(--mantine-radius-md);
box-shadow: var(--mantine-shadow-sm);
}
[data-theme="aero"] .mantine-Divider-root {
--divider-color: var(--mantine-color-default-border);
}
[data-theme="aero"] ::selection {
background: color-mix(in srgb, var(--mantine-primary-color-filled) 25%, transparent);
}
/* ---------------------------------------------------------------- */
/* 10. App component hooks in Aero language */
/* ---------------------------------------------------------------- */
/* Swipeable tab strip: flat when resting; the white-glass bar (3rd and
last blur surface, docked state only — resting pays no blur cost) */
[data-theme="aero"] .flxn-tabs[data-stuck] {
background-color: var(--aero-chrome-bg);
background-image: var(--aero-gloss-soft);
border-bottom-color: var(--aero-panel-border);
-webkit-backdrop-filter: blur(14px) saturate(150%);
backdrop-filter: blur(14px) saturate(150%);
}
[data-theme="aero"] .flxn-tab {
color: var(--mantine-color-dimmed);
font-weight: 600;
}
/* Text rides on the glossy accent pill indicator below */
[data-theme="aero"] .flxn-tab[data-active] {
color: var(--flxn-on-accent);
font-weight: 700;
}
[data-theme="aero"] .flxn-tabs-indicator {
border-bottom: none;
border-radius: 999px;
background-color: var(--mantine-primary-color-filled);
background-image: var(--aero-gloss);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45), var(--mantine-shadow-xs);
}
/* Nav grid tiles: glass panels with the cards' top-half sheen */
[data-theme="aero"] .flxn-tile {
background-color: var(--aero-panel-bg);
background-image: var(--aero-panel-sheen);
border: 1px solid var(--aero-panel-border);
border-radius: var(--mantine-radius-lg);
box-shadow: var(--mantine-shadow-sm);
}
/* Flat accent-tinted glyph */
[data-theme="aero"] .flxn-tile-icon {
color: var(--mantine-primary-color-light-color);
}
/* "See all" link: glossy accent mini pill */
[data-theme="aero"] .flxn-see-all {
border-radius: 999px;
background-image: var(--aero-gloss-soft);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35);
}
/* Match card + reaction tray read as one glass construction: same
translucent border; tray is a subtler aqua-tinted glass, no sheen. */
[data-theme="aero"] .flxn-match-card {
background-color: var(--aero-panel-bg);
background-image: var(--aero-panel-sheen);
border: 1px solid var(--aero-panel-border);
box-shadow: var(--mantine-shadow-md);
}
[data-theme="aero"] .flxn-reaction-row {
background-color: var(--aero-track);
background-image: none;
border: 1px solid var(--aero-panel-border);
border-radius: var(--mantine-radius-md);
box-shadow: var(--mantine-shadow-xs);
}
/* Glossy bezel: soft drop under the frame; the single ring itself comes
from --flxn-avatar-border */
[data-theme="aero"] .flxn-avatar-frame {
box-shadow: var(--mantine-shadow-xs);
}
/* ---------------------------------------------------------------- */
/* 11. Fallbacks and reduced transparency */
/* ---------------------------------------------------------------- */
@supports not (backdrop-filter: blur(1px)) {
[data-theme="aero"] .mantine-AppShell-header,
[data-theme="aero"] .mantine-AppShell-navbar,
[data-theme="aero"] .flxn-tabs[data-stuck] {
background-color: var(--aero-solid);
}
}
@media (prefers-reduced-transparency: reduce) {
[data-theme="aero"][data-theme][data-mantine-color-scheme="light"] {
--aero-panel-bg: #ffffff;
--aero-panel-border: rgba(125, 178, 212, 0.55);
--aero-chrome-bg: #f4fafe;
--aero-input-bg: #ffffff;
--aero-track: #d5e8f4;
--aero-indicator-bg: #ffffff;
--mantine-color-default: #ffffff;
--mantine-color-default-hover: #e6f2fa;
--flxn-showcase-bg: #ffffff;
--flxn-podium-1-bg: #fdf3d2;
--flxn-podium-2-bg: #f2f7fb;
--flxn-podium-3-bg: #fbe8d8;
--flxn-overlay: rgba(10, 45, 70, 0.6);
}
[data-theme="aero"][data-theme][data-mantine-color-scheme="dark"] {
--aero-panel-bg: #0d2a3e;
--aero-chrome-bg: #0a2536;
--aero-input-bg: #061a28;
--aero-track: #0a2334;
--aero-indicator-bg: #1f648c;
--mantine-color-default: #133a52;
--mantine-color-default-hover: #1b4a66;
--flxn-showcase-bg: #0d2a3e;
--flxn-podium-1-bg: #23303a;
--flxn-podium-2-bg: #1a2c39;
--flxn-podium-3-bg: #222c36;
--flxn-overlay: rgba(0, 8, 14, 0.75);
}
[data-theme="aero"] .mantine-AppShell-header,
[data-theme="aero"] .mantine-AppShell-navbar,
[data-theme="aero"] .flxn-tabs[data-stuck] {
-webkit-backdrop-filter: none;
backdrop-filter: none;
background-color: var(--aero-solid);
}
}
+60
View File
@@ -0,0 +1,60 @@
/* App-level semantic tokens; themes restyle them under [data-theme] scopes.
No light-dark() in html-level declarations — postcss-preset-mantine
rewrites it into a descendant selector that never matches <html>. Use
explicit [data-mantine-color-scheme] blocks. */
:root {
/* accolades */
--flxn-gold: gold;
--flxn-silver: var(--mantine-color-gray-5);
--flxn-bronze: var(--mantine-color-orange-6);
/* toast icons */
--flxn-toast-success: lightgreen;
--flxn-toast-error: lightcoral;
/* surfaces & decoration */
--flxn-overlay: rgba(0, 0, 0, 0.4);
--flxn-dotgrid-color: var(--mantine-color-default-border);
--flxn-pop-shadow: rgba(0, 0, 0, 0.3);
--flxn-avatar-radius: 50%;
--flxn-avatar-border: 1px solid var(--mantine-color-default-border);
--flxn-badge-radius: 12px;
/* text on accent fills: black/white from the cascaded fill (oklch block
below overrides this fallback) */
--flxn-on-accent: var(--mantine-primary-color-contrast);
/* light-scheme values */
--flxn-podium-1-bg: color-mix(in srgb, var(--mantine-color-yellow-4) 10%, var(--mantine-color-white));
--flxn-podium-1-border: color-mix(in srgb, var(--mantine-color-yellow-6) 45%, var(--mantine-color-gray-3));
--flxn-podium-1-label: var(--mantine-color-yellow-8);
--flxn-podium-2-bg: color-mix(in srgb, var(--mantine-color-gray-5) 10%, var(--mantine-color-white));
--flxn-podium-2-border: var(--mantine-color-gray-4);
--flxn-podium-2-label: var(--mantine-color-gray-6);
--flxn-podium-3-bg: color-mix(in srgb, var(--mantine-color-orange-5) 8%, var(--mantine-color-white));
--flxn-podium-3-border: color-mix(in srgb, var(--mantine-color-orange-6) 35%, var(--mantine-color-gray-3));
--flxn-podium-3-label: var(--mantine-color-orange-8);
--flxn-showcase-bg: var(--mantine-color-white);
}
:root[data-mantine-color-scheme="dark"] {
--flxn-podium-1-bg: color-mix(in srgb, var(--mantine-color-yellow-8) 16%, var(--mantine-color-dark-6));
--flxn-podium-1-border: color-mix(in srgb, var(--mantine-color-yellow-6) 35%, var(--mantine-color-dark-4));
--flxn-podium-1-label: var(--mantine-color-yellow-4);
--flxn-podium-2-bg: color-mix(in srgb, var(--mantine-color-gray-5) 10%, var(--mantine-color-dark-6));
--flxn-podium-2-border: var(--mantine-color-dark-4);
--flxn-podium-2-label: var(--mantine-color-gray-4);
--flxn-podium-3-bg: color-mix(in srgb, var(--mantine-color-orange-9) 14%, var(--mantine-color-dark-6));
--flxn-podium-3-border: color-mix(in srgb, var(--mantine-color-orange-7) 30%, var(--mantine-color-dark-4));
--flxn-podium-3-label: var(--mantine-color-orange-4);
--flxn-showcase-bg: var(--mantine-color-dark-7);
}
@supports (color: oklch(from red calc(1 - 1) 0 h)) {
:root {
--flxn-on-accent: oklch(
from var(--mantine-primary-color-filled) calc((0.6 - l) * infinity) 0 h
);
}
}
+17
View File
@@ -0,0 +1,17 @@
import { msg } from "@lingui/core/macro";
import type { ThemeManifest } from "../types";
const manifest: ThemeManifest = {
id: "bauhaus",
label: msg`Bauhaus`,
colorSchemes: "both",
fontLinks: [
"https://fonts.googleapis.com/css2?family=Jost:ital,wght@0,100..900;1,100..900&display=swap",
],
chromeColors: {
light: { base: "#f4f1ea", dimmed: "#93918a" },
dark: { base: "#0d0d0d", dimmed: "#000000" },
},
};
export default manifest;
+725
View File
@@ -0,0 +1,725 @@
/* Bauhaus — 1925 Dessau. Flat planes, 2px black rules, zero radius.
Fixed decoration keeps the canonical triad (red #d42220 / blue #0050b3 /
yellow #f4cc00); everything interactive follows the user's accent via the
primary aliases, with all eight ramps retuned to poster-flat print hues. */
/* ---------- 1. Scheme-independent tokens ---------- */
[data-theme="bauhaus"][data-theme] {
--bh-red: #d42220;
--bh-blue: #0050b3;
--bh-yellow: #f4cc00;
--mantine-font-family: "Jost", "Inter", sans-serif;
--mantine-font-family-headings: "Jost", "Inter", sans-serif;
--mantine-heading-font-weight: 700;
--mantine-h1-font-weight: 800;
--mantine-h2-font-weight: 800;
--mantine-h3-font-weight: 700;
--mantine-radius-default: 0;
--mantine-radius-xs: 0;
--mantine-radius-sm: 0;
--mantine-radius-md: 0;
--mantine-radius-lg: 0;
--mantine-radius-xl: 0;
--mantine-shadow-xs: none;
--mantine-shadow-sm: none;
--mantine-shadow-md: none;
--mantine-shadow-lg: none;
--mantine-shadow-xl: none;
--flxn-avatar-radius: 0;
--flxn-avatar-border: 2px solid var(--bh-line);
--flxn-badge-radius: 0;
/* poster-flat accent ramps (all eight) */
--mantine-color-red-0: #fbe9e8;
--mantine-color-red-1: #f6d0cf;
--mantine-color-red-2: #efaba9;
--mantine-color-red-3: #e77f7c;
--mantine-color-red-4: #dd534f;
--mantine-color-red-5: #d42220;
--mantine-color-red-6: #c01e1c;
--mantine-color-red-7: #a51a18;
--mantine-color-red-8: #871512;
--mantine-color-red-9: #66100e;
--mantine-color-blue-0: #e7f0fa;
--mantine-color-blue-1: #c9dcf4;
--mantine-color-blue-2: #9dc0ea;
--mantine-color-blue-3: #6b9edd;
--mantine-color-blue-4: #3675c9;
--mantine-color-blue-5: #0050b3;
--mantine-color-blue-6: #00479f;
--mantine-color-blue-7: #003d88;
--mantine-color-blue-8: #00316d;
--mantine-color-blue-9: #002550;
--mantine-color-yellow-0: #fdf9e2;
--mantine-color-yellow-1: #fbf1bb;
--mantine-color-yellow-2: #f8e587;
--mantine-color-yellow-3: #f6d94d;
--mantine-color-yellow-4: #f5d121;
--mantine-color-yellow-5: #f4cc00;
--mantine-color-yellow-6: #dcb800;
--mantine-color-yellow-7: #c0a000;
--mantine-color-yellow-8: #a08600;
--mantine-color-yellow-9: #7d6900;
--mantine-color-green-0: #e6f3ea;
--mantine-color-green-1: #c5e3cf;
--mantine-color-green-2: #97cca9;
--mantine-color-green-3: #64b17e;
--mantine-color-green-4: #349756;
--mantine-color-green-5: #0f7f36;
--mantine-color-green-6: #0d7230;
--mantine-color-green-7: #0b6229;
--mantine-color-green-8: #095021;
--mantine-color-green-9: #063d19;
--mantine-color-grape-0: #f2eaf7;
--mantine-color-grape-1: #e0cfec;
--mantine-color-grape-2: #c6a8da;
--mantine-color-grape-3: #a87cc4;
--mantine-color-grape-4: #8952aa;
--mantine-color-grape-5: #6a2c91;
--mantine-color-grape-6: #5f2782;
--mantine-color-grape-7: #522170;
--mantine-color-grape-8: #431b5c;
--mantine-color-grape-9: #331546;
--mantine-color-orange-0: #fdefe2;
--mantine-color-orange-1: #fadcbd;
--mantine-color-orange-2: #f6c28d;
--mantine-color-orange-3: #f1a355;
--mantine-color-orange-4: #ec8524;
--mantine-color-orange-5: #e86a00;
--mantine-color-orange-6: #d05f00;
--mantine-color-orange-7: #b25100;
--mantine-color-orange-8: #924300;
--mantine-color-orange-9: #703300;
--mantine-color-pink-0: #fbe9f1;
--mantine-color-pink-1: #f6cede;
--mantine-color-pink-2: #efa5c3;
--mantine-color-pink-3: #e675a2;
--mantine-color-pink-4: #dd4784;
--mantine-color-pink-5: #d6246e;
--mantine-color-pink-6: #c02063;
--mantine-color-pink-7: #a51b55;
--mantine-color-pink-8: #871646;
--mantine-color-pink-9: #661134;
--mantine-color-lime-0: #f4f7e3;
--mantine-color-lime-1: #e6edbe;
--mantine-color-lime-2: #d2df8d;
--mantine-color-lime-3: #bccd55;
--mantine-color-lime-4: #a5bd24;
--mantine-color-lime-5: #8fae00;
--mantine-color-lime-6: #819c00;
--mantine-color-lime-7: #6f8700;
--mantine-color-lime-8: #5c7000;
--mantine-color-lime-9: #475600;
}
/* ---------- 2. Per-scheme tokens ---------- */
[data-theme="bauhaus"][data-theme][data-mantine-color-scheme="light"] {
--bh-line: #000000;
--bh-plane: #f4f1ea;
--bh-ink: #000000;
--bh-ink-contrast: #f4f1ea;
--bh-focus: var(--mantine-primary-color-filled);
--bh-tray: #eae6db;
--mantine-color-body: #f4f1ea;
--mantine-color-text: #000000;
--mantine-color-bright: #000000;
--mantine-color-dimmed: #52504a;
--mantine-color-placeholder: #6f6c64;
--mantine-color-anchor: #0050b3;
--mantine-color-error: #c01e1c;
--mantine-color-default: #f4f1ea;
--mantine-color-default-hover: #eae6db;
--mantine-color-default-color: #000000;
--mantine-color-default-border: #000000;
--mantine-color-red-filled: #d42220;
--mantine-color-red-filled-hover: #b81d1b;
--mantine-color-red-contrast: #ffffff;
--mantine-color-red-light: rgba(212, 34, 32, 0.1);
--mantine-color-red-light-hover: rgba(212, 34, 32, 0.16);
--mantine-color-red-light-color: #b81d1b;
--mantine-color-red-outline: #c01e1c;
--mantine-color-red-outline-hover: rgba(212, 34, 32, 0.1);
--mantine-color-red-text: #c01e1c;
--mantine-color-blue-filled: #0050b3;
--mantine-color-blue-filled-hover: #00479f;
--mantine-color-blue-contrast: #ffffff;
--mantine-color-blue-light: rgba(0, 80, 179, 0.1);
--mantine-color-blue-light-hover: rgba(0, 80, 179, 0.16);
--mantine-color-blue-light-color: #0050b3;
--mantine-color-blue-outline: #0050b3;
--mantine-color-blue-outline-hover: rgba(0, 80, 179, 0.1);
--mantine-color-blue-text: #0050b3;
--mantine-color-yellow-filled: #f4cc00;
--mantine-color-yellow-filled-hover: #dcb800;
--mantine-color-yellow-contrast: #000000;
--mantine-color-yellow-light: rgba(244, 204, 0, 0.16);
--mantine-color-yellow-light-hover: rgba(244, 204, 0, 0.26);
--mantine-color-yellow-light-color: #8a7300;
--mantine-color-yellow-outline: #8a7300;
--mantine-color-yellow-outline-hover: rgba(244, 204, 0, 0.16);
--mantine-color-yellow-text: #8a7300;
--mantine-color-green-filled: #0f7f36;
--mantine-color-green-filled-hover: #0d7230;
--mantine-color-green-contrast: #ffffff;
--mantine-color-green-light: rgba(15, 127, 54, 0.1);
--mantine-color-green-light-hover: rgba(15, 127, 54, 0.16);
--mantine-color-green-light-color: #0b6229;
--mantine-color-green-outline: #0d7230;
--mantine-color-green-outline-hover: rgba(15, 127, 54, 0.1);
--mantine-color-green-text: #0d7230;
--mantine-color-grape-filled: #6a2c91;
--mantine-color-grape-filled-hover: #5f2782;
--mantine-color-grape-contrast: #ffffff;
--mantine-color-grape-light: rgba(106, 44, 145, 0.1);
--mantine-color-grape-light-hover: rgba(106, 44, 145, 0.16);
--mantine-color-grape-light-color: #5f2782;
--mantine-color-grape-outline: #5f2782;
--mantine-color-grape-outline-hover: rgba(106, 44, 145, 0.1);
--mantine-color-grape-text: #5f2782;
--mantine-color-orange-filled: #e86a00;
--mantine-color-orange-filled-hover: #d05f00;
--mantine-color-orange-contrast: #000000;
--mantine-color-orange-light: rgba(232, 106, 0, 0.12);
--mantine-color-orange-light-hover: rgba(232, 106, 0, 0.18);
--mantine-color-orange-light-color: #b25100;
--mantine-color-orange-outline: #b25100;
--mantine-color-orange-outline-hover: rgba(232, 106, 0, 0.12);
--mantine-color-orange-text: #b25100;
--mantine-color-pink-filled: #d6246e;
--mantine-color-pink-filled-hover: #c02063;
--mantine-color-pink-contrast: #ffffff;
--mantine-color-pink-light: rgba(214, 36, 110, 0.1);
--mantine-color-pink-light-hover: rgba(214, 36, 110, 0.16);
--mantine-color-pink-light-color: #c02063;
--mantine-color-pink-outline: #c02063;
--mantine-color-pink-outline-hover: rgba(214, 36, 110, 0.1);
--mantine-color-pink-text: #c02063;
--mantine-color-lime-filled: #8fae00;
--mantine-color-lime-filled-hover: #819c00;
--mantine-color-lime-contrast: #000000;
--mantine-color-lime-light: rgba(143, 174, 0, 0.14);
--mantine-color-lime-light-hover: rgba(143, 174, 0, 0.22);
--mantine-color-lime-light-color: #5c7000;
--mantine-color-lime-outline: #5c7000;
--mantine-color-lime-outline-hover: rgba(143, 174, 0, 0.14);
--mantine-color-lime-text: #5c7000;
/* app tokens */
--flxn-gold: #d9b500;
--flxn-silver: #8d8a80;
--flxn-bronze: #a05a2c;
--flxn-toast-success: #2b8a3e;
--flxn-toast-error: #c01e1c;
--flxn-podium-1-bg: #f4cc00;
--flxn-podium-1-border: #000000;
--flxn-podium-1-label: #000000;
--flxn-podium-2-bg: #c9dcf4;
--flxn-podium-2-border: #000000;
--flxn-podium-2-label: #003d88;
--flxn-podium-3-bg: #f6d0cf;
--flxn-podium-3-border: #000000;
--flxn-podium-3-label: #a51a18;
--flxn-showcase-bg: #faf8f2;
--flxn-overlay: rgba(0, 0, 0, 0.5);
--flxn-dotgrid-color: rgba(0, 0, 0, 0.18);
--flxn-pop-shadow: rgba(0, 0, 0, 0);
}
[data-theme="bauhaus"][data-theme][data-mantine-color-scheme="dark"] {
--bh-line: #f4f1ea;
--bh-plane: #0d0d0d;
--bh-ink: #f4f1ea;
--bh-ink-contrast: #000000;
--bh-focus: var(--mantine-primary-color-4);
--bh-tray: #1d1c19;
--mantine-color-body: #0d0d0d;
--mantine-color-text: #f4f1ea;
--mantine-color-bright: #ffffff;
--mantine-color-dimmed: #a5a196;
--mantine-color-placeholder: #8b877c;
--mantine-color-anchor: #7fb0e8;
--mantine-color-error: #ef6b64;
--mantine-color-default: #0d0d0d;
--mantine-color-default-hover: #1d1c19;
--mantine-color-default-color: #f4f1ea;
--mantine-color-default-border: #f4f1ea;
--mantine-color-red-filled: #d42220;
--mantine-color-red-filled-hover: #b81d1b;
--mantine-color-red-contrast: #ffffff;
--mantine-color-red-light: rgba(229, 83, 79, 0.16);
--mantine-color-red-light-hover: rgba(229, 83, 79, 0.24);
--mantine-color-red-light-color: #f08984;
--mantine-color-red-outline: #ef6b64;
--mantine-color-red-outline-hover: rgba(229, 83, 79, 0.16);
--mantine-color-red-text: #ef6b64;
--mantine-color-blue-filled: #0050b3;
--mantine-color-blue-filled-hover: #00479f;
--mantine-color-blue-contrast: #ffffff;
--mantine-color-blue-light: rgba(107, 158, 221, 0.16);
--mantine-color-blue-light-hover: rgba(107, 158, 221, 0.24);
--mantine-color-blue-light-color: #9dc0ea;
--mantine-color-blue-outline: #7fb0e8;
--mantine-color-blue-outline-hover: rgba(107, 158, 221, 0.16);
--mantine-color-blue-text: #7fb0e8;
--mantine-color-yellow-filled: #f4cc00;
--mantine-color-yellow-filled-hover: #dcb800;
--mantine-color-yellow-contrast: #000000;
--mantine-color-yellow-light: rgba(244, 204, 0, 0.14);
--mantine-color-yellow-light-hover: rgba(244, 204, 0, 0.22);
--mantine-color-yellow-light-color: #f4cc00;
--mantine-color-yellow-outline: #f4cc00;
--mantine-color-yellow-outline-hover: rgba(244, 204, 0, 0.14);
--mantine-color-yellow-text: #f4cc00;
--mantine-color-green-filled: #0f7f36;
--mantine-color-green-filled-hover: #0d7230;
--mantine-color-green-contrast: #ffffff;
--mantine-color-green-light: rgba(100, 177, 126, 0.16);
--mantine-color-green-light-hover: rgba(100, 177, 126, 0.24);
--mantine-color-green-light-color: #97cca9;
--mantine-color-green-outline: #64b17e;
--mantine-color-green-outline-hover: rgba(100, 177, 126, 0.16);
--mantine-color-green-text: #64b17e;
--mantine-color-grape-filled: #6a2c91;
--mantine-color-grape-filled-hover: #5f2782;
--mantine-color-grape-contrast: #ffffff;
--mantine-color-grape-light: rgba(168, 124, 196, 0.16);
--mantine-color-grape-light-hover: rgba(168, 124, 196, 0.24);
--mantine-color-grape-light-color: #c6a8da;
--mantine-color-grape-outline: #a87cc4;
--mantine-color-grape-outline-hover: rgba(168, 124, 196, 0.16);
--mantine-color-grape-text: #a87cc4;
--mantine-color-orange-filled: #e86a00;
--mantine-color-orange-filled-hover: #d05f00;
--mantine-color-orange-contrast: #000000;
--mantine-color-orange-light: rgba(241, 163, 85, 0.16);
--mantine-color-orange-light-hover: rgba(241, 163, 85, 0.24);
--mantine-color-orange-light-color: #f6c28d;
--mantine-color-orange-outline: #f1a355;
--mantine-color-orange-outline-hover: rgba(241, 163, 85, 0.16);
--mantine-color-orange-text: #f1a355;
--mantine-color-pink-filled: #d6246e;
--mantine-color-pink-filled-hover: #c02063;
--mantine-color-pink-contrast: #ffffff;
--mantine-color-pink-light: rgba(230, 117, 162, 0.16);
--mantine-color-pink-light-hover: rgba(230, 117, 162, 0.24);
--mantine-color-pink-light-color: #efa5c3;
--mantine-color-pink-outline: #e675a2;
--mantine-color-pink-outline-hover: rgba(230, 117, 162, 0.16);
--mantine-color-pink-text: #e675a2;
--mantine-color-lime-filled: #8fae00;
--mantine-color-lime-filled-hover: #819c00;
--mantine-color-lime-contrast: #000000;
--mantine-color-lime-light: rgba(165, 189, 36, 0.16);
--mantine-color-lime-light-hover: rgba(165, 189, 36, 0.24);
--mantine-color-lime-light-color: #bccd55;
--mantine-color-lime-outline: #a5bd24;
--mantine-color-lime-outline-hover: rgba(165, 189, 36, 0.16);
--mantine-color-lime-text: #a5bd24;
/* app tokens */
--flxn-gold: #f4cc00;
--flxn-silver: #a5a196;
--flxn-bronze: #d08048;
--flxn-toast-success: #51cf66;
--flxn-toast-error: #ef6b64;
--flxn-podium-1-bg: #332b00;
--flxn-podium-1-border: #f4cc00;
--flxn-podium-1-label: #f4cc00;
--flxn-podium-2-bg: #001a38;
--flxn-podium-2-border: #6b9edd;
--flxn-podium-2-label: #9dc0ea;
--flxn-podium-3-bg: #380b0a;
--flxn-podium-3-border: #e5534f;
--flxn-podium-3-label: #f08984;
--flxn-showcase-bg: #000000;
--flxn-overlay: rgba(0, 0, 0, 0.65);
--flxn-dotgrid-color: rgba(244, 241, 234, 0.18);
--flxn-pop-shadow: rgba(0, 0, 0, 0);
}
/* Without JS autoContrast, Mantine pins --mantine-primary-color-contrast to
white, which is unreadable on the light fills (yellow/orange/lime).
Recompute it from the accent fill's own lightness: below 0.62 OKLCH-L
gets white text, above gets black — matches the per-color -contrast
values declared above for all eight accents. */
@supports (color: oklch(from red calc((0.5 - l) * infinity) 0 h)) {
[data-theme="bauhaus"][data-theme][data-mantine-color-scheme] {
--mantine-primary-color-contrast: oklch(
from var(--mantine-primary-color-filled) calc((0.62 - l) * infinity) 0 h
);
}
}
/* ---------- 3. Structure: planes bounded by rules, never shadows ---------- */
[data-theme="bauhaus"] .mantine-Paper-root {
background-color: var(--bh-plane);
border: 2px solid var(--bh-line);
border-radius: 0;
box-shadow: none;
}
[data-theme="bauhaus"] .mantine-AppShell-root {
--app-shell-border-color: var(--bh-line);
}
[data-theme="bauhaus"] .mantine-AppShell-header {
background-color: var(--mantine-color-body);
border-bottom: 2px solid var(--bh-line);
}
[data-theme="bauhaus"] .mantine-AppShell-navbar {
background-color: var(--mantine-color-body);
border-inline-end: 2px solid var(--bh-line);
}
[data-theme="bauhaus"] .mantine-Divider-root {
--divider-color: var(--bh-line);
--divider-size: 3px;
}
/* ---------- 4. Type: uppercase, widely-tracked functional labels ---------- */
[data-theme="bauhaus"] .mantine-Button-root {
border-radius: 0;
box-shadow: none;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
}
[data-theme="bauhaus"]
.mantine-Button-root:where(
[data-variant="filled"],
[data-variant="default"],
[data-variant="outline"],
[data-variant="light"],
[data-variant="white"]
) {
border: 2px solid var(--bh-line);
}
[data-theme="bauhaus"] .mantine-ActionIcon-root {
border-radius: 0;
box-shadow: none;
}
[data-theme="bauhaus"]
.mantine-ActionIcon-root:where(
[data-variant="filled"],
[data-variant="default"],
[data-variant="outline"],
[data-variant="light"],
[data-variant="white"]
) {
border: 2px solid var(--bh-line);
}
[data-theme="bauhaus"] .mantine-InputWrapper-label {
font-size: var(--mantine-font-size-xs);
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
[data-theme="bauhaus"] .mantine-Badge-root {
border-radius: 0;
font-weight: 700;
letter-spacing: 0.08em;
}
/* ---------- 5. Inputs ---------- */
[data-theme="bauhaus"] .mantine-Input-input {
background-color: var(--bh-plane);
border: 2px solid var(--bh-line);
border-radius: 0;
}
[data-theme="bauhaus"] .mantine-Input-input:focus,
[data-theme="bauhaus"] .mantine-Input-input:focus-within {
border-color: var(--bh-focus);
outline: none;
}
[data-theme="bauhaus"] .mantine-Checkbox-input {
border: 2px solid var(--bh-line);
border-radius: 0;
}
[data-theme="bauhaus"] .mantine-Radio-radio {
border: 2px solid var(--bh-line);
}
[data-theme="bauhaus"] .mantine-Switch-track {
border: 2px solid var(--bh-line);
border-radius: 0;
}
[data-theme="bauhaus"] .mantine-Switch-thumb {
border: 2px solid var(--bh-line);
border-radius: 0;
}
/* ---------- 6. Tabs & SegmentedControl: black-line constructions ---------- */
[data-theme="bauhaus"] .mantine-Tabs-list::before {
border-color: var(--bh-line);
border-bottom-width: 2px;
}
[data-theme="bauhaus"] .mantine-Tabs-tab {
border-radius: 0;
border-width: 2px;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
}
[data-theme="bauhaus"] .mantine-Tabs-tab[data-active] {
background-color: var(--mantine-primary-color-filled);
border-color: var(--bh-line);
color: var(--mantine-primary-color-contrast);
}
[data-theme="bauhaus"] .mantine-SegmentedControl-root {
background-color: transparent;
border: 2px solid var(--bh-line);
border-radius: 0;
padding: 0;
}
[data-theme="bauhaus"] .mantine-SegmentedControl-indicator {
background-color: var(--mantine-primary-color-filled);
border-radius: 0;
box-shadow: none;
}
[data-theme="bauhaus"] .mantine-SegmentedControl-label {
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
}
[data-theme="bauhaus"] .mantine-SegmentedControl-label[data-active],
[data-theme="bauhaus"]
.mantine-SegmentedControl-control[data-active]
.mantine-SegmentedControl-label {
color: var(--mantine-primary-color-contrast);
}
/* Accent-fill on-colors: Mantine hardcodes white text on filled variants,
selected combobox options and check glyphs; pair them with the accent's
contrast color so yellow/orange/lime accents stay legible. */
[data-theme="bauhaus"] .mantine-Button-root[data-variant="filled"],
[data-theme="bauhaus"] .mantine-Button-root:not([data-variant]),
[data-theme="bauhaus"] .mantine-ActionIcon-root[data-variant="filled"],
[data-theme="bauhaus"] .mantine-ActionIcon-root:not([data-variant]),
[data-theme="bauhaus"] .mantine-Badge-root[data-variant="filled"],
[data-theme="bauhaus"] .mantine-Badge-root:not([data-variant]),
[data-theme="bauhaus"] .mantine-ThemeIcon-root[data-variant="filled"],
[data-theme="bauhaus"] .mantine-ThemeIcon-root:not([data-variant]) {
color: var(--mantine-primary-color-contrast);
}
[data-theme="bauhaus"] [data-combobox-selected] {
color: var(--mantine-primary-color-contrast);
}
[data-theme="bauhaus"] .mantine-Checkbox-root {
--checkbox-icon-color: var(--mantine-primary-color-contrast);
}
[data-theme="bauhaus"] .mantine-Radio-root {
--radio-icon-color: var(--mantine-primary-color-contrast);
}
/* ---------- 7. Overlays & feedback surfaces ---------- */
[data-theme="bauhaus"] .mantine-Modal-content {
border: 2px solid var(--bh-line);
border-radius: 0;
box-shadow: none;
}
[data-theme="bauhaus"] .mantine-Modal-header {
border-bottom: 2px solid var(--bh-line);
}
[data-theme="bauhaus"] .mantine-Popover-dropdown {
border: 2px solid var(--bh-line);
border-radius: 0;
box-shadow: none;
}
[data-theme="bauhaus"] .mantine-Tooltip-tooltip {
background-color: var(--bh-ink);
border-radius: 0;
color: var(--bh-ink-contrast);
}
[data-theme="bauhaus"] .mantine-Notification-root {
background-color: var(--bh-plane);
border: 2px solid var(--bh-line);
border-radius: 0;
box-shadow: none;
}
[data-theme="bauhaus"] .mantine-Alert-root {
border: 2px solid var(--bh-line);
border-radius: 0;
}
[data-theme="bauhaus"] .mantine-Progress-root {
background-color: transparent;
border: 2px solid var(--bh-line);
border-radius: 0;
}
[data-theme="bauhaus"] .mantine-Progress-section {
border-radius: 0;
}
[data-theme="bauhaus"] .mantine-Indicator-indicator {
border-radius: 0;
}
[data-theme="bauhaus"] .mantine-ThemeIcon-root {
border: 2px solid var(--bh-line);
border-radius: 0;
}
/* strict square avatars: frame and content square together */
[data-theme="bauhaus"] .mantine-Avatar-root {
border-radius: 0;
}
[data-theme="bauhaus"] .mantine-Avatar-image,
[data-theme="bauhaus"] .mantine-Avatar-placeholder {
border-radius: 0;
}
/* ---------- 8. App component hooks ---------- */
/* Tab strip: flat while resting (2px transparent rule keeps height stable);
docked gets the black under-rule. Active tab is a solid accent block. */
[data-theme="bauhaus"] .flxn-tabs {
border-bottom-width: 2px;
}
[data-theme="bauhaus"] .flxn-tabs[data-stuck] {
background-color: var(--mantine-color-body);
border-bottom-color: var(--bh-line);
}
[data-theme="bauhaus"] .flxn-tab {
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
border-radius: 0;
}
[data-theme="bauhaus"] .flxn-tab[data-active] {
background-color: var(--mantine-primary-color-filled);
color: var(--flxn-on-accent);
font-weight: 700;
}
[data-theme="bauhaus"] .flxn-tabs-indicator {
border-bottom: none;
}
/* Nav grid tiles: 2px-ruled flat planes */
[data-theme="bauhaus"] .flxn-tile {
background-color: var(--bh-plane);
border: 2px solid var(--bh-line);
border-radius: 0;
box-shadow: none;
}
[data-theme="bauhaus"] .flxn-tile-icon {
color: var(--mantine-primary-color-filled);
}
/* "See all" chip: flat, bordered, uppercase tracked */
[data-theme="bauhaus"] .flxn-see-all {
background-color: var(--bh-plane);
border: 2px solid var(--bh-line);
border-radius: 0;
color: var(--bh-ink);
font-size: var(--mantine-font-size-xs);
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
@media (hover: hover) {
[data-theme="bauhaus"] .flxn-see-all:hover {
background-color: var(--mantine-primary-color-light);
color: var(--bh-ink);
}
}
/* Match card + reaction tray: one construction, same 2px rules, flat fill */
[data-theme="bauhaus"] .flxn-match-card {
background-color: var(--bh-plane);
border: 2px solid var(--bh-line);
border-radius: 0;
box-shadow: none;
}
[data-theme="bauhaus"] .flxn-reaction-row {
background-color: var(--bh-tray);
border: 2px solid var(--bh-line);
border-radius: 0;
}
/* ---------- 9. Geometric heading marks: circle / square / triangle ---------- */
[data-theme="bauhaus"] h1.mantine-Title-root::before,
[data-theme="bauhaus"] h2.mantine-Title-root::before,
[data-theme="bauhaus"] h3.mantine-Title-root::before,
[data-theme="bauhaus"] h4.mantine-Title-root::before,
[data-theme="bauhaus"] h5.mantine-Title-root::before,
[data-theme="bauhaus"] h6.mantine-Title-root::before {
content: "";
display: inline-block;
width: 0.5em;
height: 0.5em;
margin-inline-end: 0.45em;
vertical-align: 0.06em;
}
[data-theme="bauhaus"] h1.mantine-Title-root::before {
background-color: var(--bh-blue);
border-radius: 50%;
}
[data-theme="bauhaus"] h2.mantine-Title-root::before {
background-color: var(--bh-red);
}
[data-theme="bauhaus"] h3.mantine-Title-root::before {
background-color: var(--bh-yellow);
clip-path: polygon(50% 0, 100% 100%, 0 100%);
}
[data-theme="bauhaus"] h4.mantine-Title-root::before {
background-color: var(--bh-red);
border-radius: 50%;
}
[data-theme="bauhaus"] h5.mantine-Title-root::before {
background-color: var(--bh-yellow);
}
[data-theme="bauhaus"] h6.mantine-Title-root::before {
background-color: var(--bh-blue);
clip-path: polygon(50% 0, 100% 100%, 0 100%);
}
/* ---------- 10. Press feedback: mechanical, not springy ---------- */
[data-theme="bauhaus"]
.flxn-press:active:not(:disabled):not([data-disabled]):not([data-loading]) {
transform: translateY(2px);
}
@media (prefers-reduced-motion: reduce) {
[data-theme="bauhaus"]
.flxn-press:active:not(:disabled):not([data-disabled]):not([data-loading]) {
transform: none;
}
}
+30
View File
@@ -0,0 +1,30 @@
import { msg } from "@lingui/core/macro";
import type { ThemeManifest } from "../types";
const manifest: ThemeManifest = {
id: "brutal",
label: msg`Brutal`,
colorSchemes: "both",
fontLinks: [
"https://fonts.googleapis.com/css2?family=Archivo+Black&family=Space+Grotesk:wght@400;500;600;700&display=swap",
],
chromeColors: {
light: { base: "#f5f2ea", dimmed: "#e6e1d1" },
dark: { base: "#111111", dimmed: "#000000" },
},
mantine: {
// Light acid accents (yellow/lime) must flip filled text to dark ink.
autoContrast: true,
defaultRadius: 0,
fontFamily: '"Space Grotesk", "Inter", sans-serif',
headings: {
fontFamily: '"Archivo Black", "League Spartan", sans-serif',
fontWeight: "400",
},
components: {
Loader: { defaultProps: { type: "bars" } },
},
},
};
export default manifest;
+645
View File
@@ -0,0 +1,645 @@
/* Neobrutalist theme: flat surfaces, zero radius, 3px ink borders, hard
offset shadows. Ink is #000 in light and pale off-white in dark. Acid
yellow is pure decoration (selection, hover flashes, podium gold); all
accent/active/focus styling follows the user's chosen primary color via
loud retuned ramps. */
/* ---------- 1. Scheme-independent tokens ---------- */
[data-theme="brutal"][data-theme] {
--brutal-yellow: #ffde59;
--mantine-font-family: "Space Grotesk", "Inter", sans-serif;
--mantine-font-family-headings: "Archivo Black", "League Spartan", sans-serif;
--mantine-heading-font-weight: 400;
--mantine-h1-font-weight: 400;
--mantine-h2-font-weight: 400;
--mantine-h3-font-weight: 400;
--mantine-h4-font-weight: 400;
--mantine-h5-font-weight: 400;
--mantine-h6-font-weight: 400;
--mantine-radius-default: 0;
--mantine-radius-xs: 0;
--mantine-radius-sm: 0;
--mantine-radius-md: 0;
--mantine-radius-lg: 0;
--mantine-radius-xl: 0;
--mantine-shadow-xs: 3px 3px 0 0 var(--brutal-ink, #000);
--mantine-shadow-sm: 5px 5px 0 0 var(--brutal-ink, #000);
--mantine-shadow-md: 6px 6px 0 0 var(--brutal-ink, #000);
--mantine-shadow-lg: 8px 8px 0 0 var(--brutal-ink, #000);
--mantine-shadow-xl: 10px 10px 0 0 var(--brutal-ink, #000);
--flxn-gold: var(--brutal-yellow);
--flxn-pop-shadow: var(--brutal-ink, #000);
--flxn-avatar-radius: 0;
--flxn-avatar-border: 3px solid var(--brutal-ink, #000);
--flxn-badge-radius: 0;
/* Accent ramps retuned flat and loud, borderline fluorescent. Shade 6
(light filled) and shade 8 (dark filled) keep the same light/dark
polarity as Mantine's defaults so autoContrast text stays legible. */
--mantine-color-blue-0: #e6eeff;
--mantine-color-blue-1: #c2d4ff;
--mantine-color-blue-2: #99b8ff;
--mantine-color-blue-3: #5c8aff;
--mantine-color-blue-4: #2e66ff;
--mantine-color-blue-5: #1a5eff;
--mantine-color-blue-6: #0047ff;
--mantine-color-blue-7: #003ae6;
--mantine-color-blue-8: #0033cc;
--mantine-color-blue-9: #002699;
--mantine-color-red-0: #ffe8e8;
--mantine-color-red-1: #ffc9c9;
--mantine-color-red-2: #ffa8a8;
--mantine-color-red-3: #ff7a7a;
--mantine-color-red-4: #ff5252;
--mantine-color-red-5: #ff2b2b;
--mantine-color-red-6: #f01e1e;
--mantine-color-red-7: #d61414;
--mantine-color-red-8: #c00d0d;
--mantine-color-red-9: #8f0707;
--mantine-color-green-0: #e4ffe9;
--mantine-color-green-1: #c0ffcf;
--mantine-color-green-2: #8fffab;
--mantine-color-green-3: #4dff85;
--mantine-color-green-4: #1af06a;
--mantine-color-green-5: #0ae862;
--mantine-color-green-6: #00e05c;
--mantine-color-green-7: #00b34a;
--mantine-color-green-8: #0a8a3c;
--mantine-color-green-9: #056629;
--mantine-color-yellow-0: #fff9db;
--mantine-color-yellow-1: #fff0a8;
--mantine-color-yellow-2: #ffe770;
--mantine-color-yellow-3: #ffde59;
--mantine-color-yellow-4: #ffd83d;
--mantine-color-yellow-5: #ffd61f;
--mantine-color-yellow-6: #ffd400;
--mantine-color-yellow-7: #f5c400;
--mantine-color-yellow-8: #f0b800;
--mantine-color-yellow-9: #cc9900;
--mantine-color-grape-0: #fbe7ff;
--mantine-color-grape-1: #f3c6ff;
--mantine-color-grape-2: #ea9dff;
--mantine-color-grape-3: #dd6bff;
--mantine-color-grape-4: #cf47f2;
--mantine-color-grape-5: #c437e9;
--mantine-color-grape-6: #b829e0;
--mantine-color-grape-7: #a21ec7;
--mantine-color-grape-8: #8c14ad;
--mantine-color-grape-9: #6b0c85;
--mantine-color-orange-0: #fff0e0;
--mantine-color-orange-1: #ffddb8;
--mantine-color-orange-2: #ffc78a;
--mantine-color-orange-3: #ffab52;
--mantine-color-orange-4: #ff9a2e;
--mantine-color-orange-5: #ff9114;
--mantine-color-orange-6: #ff8800;
--mantine-color-orange-7: #f07300;
--mantine-color-orange-8: #c95400;
--mantine-color-orange-9: #9a4000;
--mantine-color-pink-0: #ffe6f1;
--mantine-color-pink-1: #ffc2dd;
--mantine-color-pink-2: #ff99c6;
--mantine-color-pink-3: #ff66aa;
--mantine-color-pink-4: #ff4796;
--mantine-color-pink-5: #ff2e88;
--mantine-color-pink-6: #f21578;
--mantine-color-pink-7: #d80d67;
--mantine-color-pink-8: #bd0857;
--mantine-color-pink-9: #8f0642;
--mantine-color-lime-0: #f6ffdb;
--mantine-color-lime-1: #eaffad;
--mantine-color-lime-2: #dcff7a;
--mantine-color-lime-3: #c8ff42;
--mantine-color-lime-4: #b8fa1e;
--mantine-color-lime-5: #aff50f;
--mantine-color-lime-6: #a6f000;
--mantine-color-lime-7: #99e000;
--mantine-color-lime-8: #8ccc00;
--mantine-color-lime-9: #6fa300;
}
/* ---------- 2. Per-scheme tokens ---------- */
[data-theme="brutal"][data-theme][data-mantine-color-scheme="light"] {
--brutal-ink: #000000;
--brutal-focus: var(--mantine-primary-color-filled);
--mantine-color-body: #f5f2ea;
--mantine-color-text: #111111;
--mantine-color-bright: #000000;
--mantine-color-dimmed: #575145;
--mantine-color-placeholder: #6b6557;
/* shade 8, not 6: fluorescent accents (yellow/lime/green) are unreadable
as link text on the light body at shade 6 */
--mantine-color-anchor: var(--mantine-primary-color-8);
--mantine-color-error: #d90429;
--mantine-color-default: #f5f2ea;
--mantine-color-default-hover: var(--brutal-yellow);
--mantine-color-default-color: #111111;
--mantine-color-default-border: #000000;
--flxn-silver: #767066;
--flxn-bronze: #b45f06;
--flxn-toast-success: #087f2c;
--flxn-toast-error: #d90429;
--flxn-podium-1-bg: var(--brutal-yellow);
--flxn-podium-1-border: #000000;
--flxn-podium-1-label: #111111;
--flxn-podium-2-bg: #d9d4c6;
--flxn-podium-2-border: #000000;
--flxn-podium-2-label: #111111;
--flxn-podium-3-bg: #ff9d5c;
--flxn-podium-3-border: #000000;
--flxn-podium-3-label: #111111;
--flxn-showcase-bg: #fffdf5;
--flxn-overlay: rgba(0, 0, 0, 0.5);
--flxn-dotgrid-color: rgba(0, 0, 0, 0.22);
}
[data-theme="brutal"][data-theme][data-mantine-color-scheme="dark"] {
--brutal-ink: #ece7da;
--brutal-focus: var(--mantine-primary-color-4);
--mantine-color-body: #111111;
--mantine-color-text: #f5f2ea;
--mantine-color-bright: #ffffff;
--mantine-color-dimmed: #a39d8c;
--mantine-color-placeholder: #9a947f;
--mantine-color-anchor: var(--mantine-primary-color-4);
--mantine-color-error: #ff4d6d;
--mantine-color-default: #111111;
--mantine-color-default-hover: #26241c;
--mantine-color-default-color: #f5f2ea;
--mantine-color-default-border: #ece7da;
--flxn-silver: #b8b2a4;
--flxn-bronze: #e08e45;
--flxn-toast-success: #3ddc73;
--flxn-toast-error: #ff4d6d;
--flxn-podium-1-bg: #292110;
--flxn-podium-1-border: var(--brutal-yellow);
--flxn-podium-1-label: var(--brutal-yellow);
--flxn-podium-2-bg: #21201b;
--flxn-podium-2-border: #b8b2a4;
--flxn-podium-2-label: #cfc9bb;
--flxn-podium-3-bg: #271a10;
--flxn-podium-3-border: #e08e45;
--flxn-podium-3-label: #ffb47a;
--flxn-showcase-bg: #191813;
--flxn-overlay: rgba(0, 0, 0, 0.65);
--flxn-dotgrid-color: rgba(236, 231, 218, 0.16);
--flxn-pop-shadow: rgba(236, 231, 218, 0.55);
}
/* ---------- 3. Type ---------- */
[data-theme="brutal"] :is(h1, h2, h3, h4, h5, h6) {
text-transform: uppercase;
letter-spacing: -0.02em;
}
[data-theme="brutal"] .mantine-Title-root {
text-transform: uppercase;
}
[data-theme="brutal"] *::selection {
background: var(--brutal-yellow);
color: #111111;
}
[data-theme="brutal"] *:focus-visible:not(.mantine-Input-input) {
outline: 3px solid var(--brutal-focus);
outline-offset: 2px;
}
[data-theme="brutal"] .mantine-Anchor-root {
font-weight: 600;
text-decoration-thickness: 2px;
text-underline-offset: 2px;
}
/* ---------- 4. Press feedback: slide into the shadow ---------- */
[data-theme="brutal"] .flxn-press {
transition:
transform 100ms cubic-bezier(0.32, 0.72, 0, 1),
box-shadow 100ms cubic-bezier(0.32, 0.72, 0, 1);
}
[data-theme="brutal"] .flxn-press:active:not(:disabled):not([data-disabled]):not([data-loading]) {
transform: translate(4px, 4px);
box-shadow: 0 0 0 0 var(--brutal-ink);
}
@media (prefers-reduced-motion: reduce) {
[data-theme="brutal"] .flxn-press,
[data-theme="brutal"] .flxn-press:active:not(:disabled):not([data-disabled]):not([data-loading]) {
transition: none;
transform: none;
}
}
/* ---------- 5. Buttons & ActionIcons ---------- */
[data-theme="brutal"] .mantine-Button-root {
border: 3px solid var(--brutal-ink);
border-radius: 0;
box-shadow: 4px 4px 0 0 var(--brutal-ink);
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.03em;
}
[data-theme="brutal"] .mantine-Button-root[data-variant="gradient"] {
background: var(--mantine-primary-color-filled);
}
[data-theme="brutal"] .mantine-ActionIcon-root {
border: 2px solid var(--brutal-ink);
border-radius: 0;
box-shadow: 3px 3px 0 0 var(--brutal-ink);
}
[data-theme="brutal"] .mantine-Button-root[data-variant="subtle"],
[data-theme="brutal"] .mantine-Button-root[data-variant="transparent"],
[data-theme="brutal"] .mantine-ActionIcon-root[data-variant="subtle"],
[data-theme="brutal"] .mantine-ActionIcon-root[data-variant="transparent"] {
border-color: transparent;
box-shadow: none;
}
[data-theme="brutal"] .mantine-Button-root:disabled,
[data-theme="brutal"] .mantine-Button-root[data-disabled],
[data-theme="brutal"] .mantine-ActionIcon-root:disabled,
[data-theme="brutal"] .mantine-ActionIcon-root[data-disabled] {
box-shadow: none;
}
[data-theme="brutal"] .mantine-CloseButton-root {
border-radius: 0;
}
/* ---------- 6. Surfaces: Paper, Card, Modal, Drawer, dropdowns ---------- */
[data-theme="brutal"] .mantine-Paper-root {
border: 3px solid var(--brutal-ink);
border-radius: 0;
}
[data-theme="brutal"] .mantine-Card-root {
box-shadow: var(--mantine-shadow-sm);
background: var(--flxn-showcase-bg);
}
[data-theme="brutal"] .mantine-Modal-content,
[data-theme="brutal"] .mantine-Drawer-content {
border: 3px solid var(--brutal-ink);
border-radius: 0;
box-shadow: var(--mantine-shadow-lg);
}
[data-theme="brutal"] .mantine-Modal-header {
background: var(--mantine-color-body);
border-bottom: 3px solid var(--brutal-ink);
}
[data-theme="brutal"] .mantine-Modal-title {
font-family: var(--mantine-font-family-headings);
text-transform: uppercase;
font-size: var(--mantine-font-size-lg);
}
[data-theme="brutal"] .mantine-Popover-dropdown,
[data-theme="brutal"] .mantine-Combobox-dropdown,
[data-theme="brutal"] .mantine-Menu-dropdown {
border: 3px solid var(--brutal-ink);
border-radius: 0;
box-shadow: var(--mantine-shadow-md);
}
[data-theme="brutal"] .mantine-Menu-item,
[data-theme="brutal"] .mantine-Combobox-option {
border-radius: 0;
font-weight: 600;
}
/* Hover flash stays decorative acid yellow; the SELECTED option is the
accent and follows the user's primary color. */
[data-theme="brutal"] .mantine-Menu-item:hover,
[data-theme="brutal"] .mantine-Menu-item[data-hovered],
[data-theme="brutal"] .mantine-Combobox-option:hover {
background: var(--brutal-yellow);
color: #111111;
}
[data-theme="brutal"] .mantine-Combobox-option[data-combobox-selected] {
background: var(--mantine-primary-color-filled);
color: var(--mantine-primary-color-contrast);
}
[data-theme="brutal"] .mantine-Tooltip-tooltip {
border-radius: 0;
background: var(--brutal-ink);
color: var(--mantine-color-body);
font-weight: 600;
}
/* ---------- 7. Inputs ---------- */
[data-theme="brutal"] .mantine-Input-input {
border-width: 3px;
border-style: solid;
border-color: var(--brutal-ink);
border-radius: 0;
background: var(--mantine-color-body);
font-weight: 500;
}
[data-theme="brutal"] .mantine-Input-input:focus,
[data-theme="brutal"] .mantine-Input-input:focus-within {
outline: none;
border-color: var(--brutal-ink);
box-shadow: 4px 4px 0 0 var(--brutal-ink);
}
[data-theme="brutal"] .mantine-Input-input[data-error] {
border-color: var(--mantine-color-error);
}
[data-theme="brutal"] :is(
.mantine-TextInput-label,
.mantine-Textarea-label,
.mantine-NumberInput-label,
.mantine-PasswordInput-label,
.mantine-Select-label,
.mantine-Autocomplete-label,
.mantine-MultiSelect-label,
.mantine-DateTimePicker-label,
.mantine-DatePickerInput-label,
.mantine-PinInput-label
) {
text-transform: uppercase;
font-weight: 700;
letter-spacing: 0.04em;
}
/* ---------- 8. Badges, indicators, avatars, icons ---------- */
[data-theme="brutal"] .mantine-Badge-root {
border: 2px solid var(--brutal-ink);
border-radius: 0;
box-shadow: 2px 2px 0 0 var(--brutal-ink);
font-weight: 800;
letter-spacing: 0.05em;
}
[data-theme="brutal"] .mantine-Indicator-indicator {
border-radius: 0;
border: 2px solid var(--brutal-ink);
}
[data-theme="brutal"] .mantine-ThemeIcon-root {
border-radius: 0;
border: 2px solid var(--brutal-ink);
}
[data-theme="brutal"] .mantine-Skeleton-root {
border-radius: 0;
}
/* ---------- 9. Tabs, NavLink, SegmentedControl: solid blocks ---------- */
[data-theme="brutal"] .mantine-Tabs-tab {
border: 3px solid transparent;
border-radius: 0;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.03em;
}
[data-theme="brutal"] .mantine-Tabs-tab[data-active] {
background: var(--mantine-primary-color-filled);
color: var(--mantine-primary-color-contrast);
border-color: var(--brutal-ink);
box-shadow: 3px 3px 0 0 var(--brutal-ink);
}
[data-theme="brutal"] .mantine-Tabs-list::before {
border-color: var(--brutal-ink);
}
[data-theme="brutal"] .mantine-NavLink-root {
border: 3px solid transparent;
border-radius: 0;
font-weight: 600;
}
[data-theme="brutal"] .mantine-NavLink-root[data-active] {
background: var(--mantine-primary-color-filled);
color: var(--mantine-primary-color-contrast);
border-color: var(--brutal-ink);
box-shadow: 4px 4px 0 0 var(--brutal-ink);
}
[data-theme="brutal"] .mantine-NavLink-root[data-active] :is(
.mantine-NavLink-label,
.mantine-NavLink-description,
.mantine-NavLink-section
) {
color: var(--mantine-primary-color-contrast);
}
[data-theme="brutal"] .mantine-SegmentedControl-root {
border: 3px solid var(--brutal-ink);
border-radius: 0;
background: var(--mantine-color-body);
}
[data-theme="brutal"] .mantine-SegmentedControl-control,
[data-theme="brutal"] .mantine-SegmentedControl-label {
border-radius: 0;
}
[data-theme="brutal"] .mantine-SegmentedControl-label {
text-transform: uppercase;
font-weight: 700;
}
[data-theme="brutal"] .mantine-SegmentedControl-indicator {
border-radius: 0;
background: var(--mantine-primary-color-filled);
border: 2px solid var(--brutal-ink);
box-shadow: none;
}
[data-theme="brutal"] .mantine-SegmentedControl-label[data-active],
[data-theme="brutal"] .mantine-SegmentedControl-control[data-active] .mantine-SegmentedControl-label {
color: var(--mantine-primary-color-contrast);
}
/* ---------- 10. Controls: Switch, Checkbox, Radio, Progress ---------- */
[data-theme="brutal"] .mantine-Switch-track {
border: 3px solid var(--brutal-ink);
border-radius: 0;
}
[data-theme="brutal"] .mantine-Switch-thumb {
border: 2px solid var(--brutal-ink);
border-radius: 0;
background: #ffffff;
}
[data-theme="brutal"] .mantine-Checkbox-input {
border: 3px solid var(--brutal-ink);
border-radius: 0;
}
[data-theme="brutal"] .mantine-Radio-radio {
border: 3px solid var(--brutal-ink);
}
[data-theme="brutal"] .mantine-Progress-root {
border: 2px solid var(--brutal-ink);
border-radius: 0;
background: var(--mantine-color-body);
}
[data-theme="brutal"] .mantine-Progress-section {
border-radius: 0;
}
/* ---------- 11. Feedback: Alert, Notification ---------- */
[data-theme="brutal"] .mantine-Alert-root {
border: 3px solid var(--brutal-ink);
border-radius: 0;
box-shadow: var(--mantine-shadow-xs);
}
[data-theme="brutal"] .mantine-Notification-root {
border: 3px solid var(--brutal-ink);
border-radius: 0;
box-shadow: var(--mantine-shadow-md);
background: var(--mantine-color-body);
}
[data-theme="brutal"] .mantine-Notification-title {
font-weight: 700;
text-transform: uppercase;
}
/* ---------- 12. Shell & structure ---------- */
[data-theme="brutal"] .mantine-AppShell-root {
--app-shell-border-color: var(--brutal-ink);
}
[data-theme="brutal"] .mantine-AppShell-header {
background: var(--mantine-color-body);
border-bottom: 3px solid var(--brutal-ink);
}
[data-theme="brutal"] .mantine-AppShell-navbar,
[data-theme="brutal"] .mantine-AppShell-aside {
background: var(--mantine-color-body);
}
[data-theme="brutal"] .mantine-Divider-root {
--divider-color: var(--brutal-ink);
--divider-size: 2px;
}
/* ---------- 13. App component hooks ---------- */
/* Tab strip: flat while resting; docking rules the ink baseline (width is
constant so pinning never shifts layout). Active tab is a solid accent
block with ink border + hard shadow — the floating underline indicator
is suppressed, the block IS the indicator. Constant transparent border
keeps tab boxes the same size in both active states. */
[data-theme="brutal"] .flxn-tabs {
border-bottom-width: 3px;
}
[data-theme="brutal"] .flxn-tabs[data-stuck] {
background-color: var(--mantine-color-body);
border-bottom-color: var(--brutal-ink);
}
[data-theme="brutal"] .flxn-tab.flxn-tab {
border: 3px solid transparent;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.03em;
}
[data-theme="brutal"] .flxn-tab.flxn-tab[data-active] {
background-color: var(--mantine-primary-color-filled);
color: var(--flxn-on-accent);
border-color: var(--brutal-ink);
box-shadow: 2px 2px 0 0 var(--brutal-ink);
font-weight: 700;
}
[data-theme="brutal"] .flxn-tabs-indicator.flxn-tabs-indicator {
border-bottom: none;
}
/* Nav grid tiles: same construction as cards — ink border, hard offset
shadow, flat accent glyph. Press sinks into the shadow. */
[data-theme="brutal"] .flxn-tile.flxn-tile {
border: 3px solid var(--brutal-ink);
border-radius: 0;
background-color: var(--flxn-showcase-bg);
box-shadow: var(--mantine-shadow-xs);
}
@media (prefers-reduced-motion: no-preference) {
[data-theme="brutal"] .flxn-tile.flxn-tile:active:not(:disabled) {
transform: translate(3px, 3px);
box-shadow: 0 0 0 0 var(--brutal-ink);
}
}
[data-theme="brutal"] .flxn-tile-icon.flxn-tile-icon {
color: var(--mantine-primary-color-filled);
}
/* "See all" chip: bordered block with a mini offset shadow; accent tint
comes through from hooks.css. */
[data-theme="brutal"] .flxn-see-all.flxn-see-all {
border: 2px solid var(--brutal-ink);
border-radius: 0;
box-shadow: 2px 2px 0 0 var(--brutal-ink);
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.03em;
}
/* Match card + reaction tray: one construction — identical 3px ink
borders, card on the raised showcase surface, tray a body-toned step
below carrying the construction's hard shadow at its bottom edge. */
[data-theme="brutal"] .flxn-match-card.flxn-match-card {
border: 3px solid var(--brutal-ink);
border-radius: 0;
background-color: var(--flxn-showcase-bg);
}
[data-theme="brutal"] .flxn-reaction-row.flxn-reaction-row {
border: 3px solid var(--brutal-ink);
border-radius: 0;
background-color: var(--mantine-color-body);
box-shadow: var(--mantine-shadow-xs);
}
+165
View File
@@ -0,0 +1,165 @@
import { msg } from "@lingui/core/macro";
import type { ThemeManifest } from "../types";
/* Instrument-panel indicator ramps — 1970s NASA/Braun control-deck lamps.
Declared in JS so Mantine regenerates every derived variant var (-filled,
-light rgba, autoContrast picks) from the retuned shades. Shade 6 is the
fill on the light beige enclosure (deep enough for white/ink autoContrast
to pick legibly); shade 5 is the fill on the powered-down dark deck
(brighter, reads as a lit lamp). */
const cassetteColors = {
// scope blue — telemetry readout
blue: [
"#eef3f7",
"#dae6ef",
"#bfd4e3",
"#9cbcd2",
"#74a0bd",
"#5586a8",
"#416d8d",
"#345a75",
"#29485e",
"#1f3748",
],
// alert red — warning lamp
red: [
"#fbecea",
"#f5d3cd",
"#edb3a8",
"#e28d7c",
"#d46c56",
"#c0503a",
"#a43f2d",
"#863323",
"#6a281b",
"#4e1e14",
],
// avocado LED — armed/ready lamp
green: [
"#eef4e6",
"#dbe8ca",
"#c2d8a5",
"#a4c47a",
"#86ac56",
"#6d9440",
"#587a34",
"#47632a",
"#384d21",
"#293818",
],
// amber — the classic panel indicator
yellow: [
"#fbf3dc",
"#f6e6b0",
"#f0d47c",
"#e9c04c",
"#e0ac2c",
"#cd9420",
"#ac7c19",
"#8c6414",
"#6d4d10",
"#4f380b",
],
// UV violet — auxiliary system lamp
grape: [
"#f2edf6",
"#e2d5ec",
"#ccb4dc",
"#b18fc9",
"#9570b3",
"#7c589c",
"#654682",
"#513869",
"#3f2b52",
"#2e1f3c",
],
// signal orange — caution lamp
orange: [
"#faede3",
"#f4d3bd",
"#ecb48c",
"#e2925c",
"#d47237",
"#bd5c22",
"#9e4b1b",
"#813d17",
"#653012",
"#4a230d",
],
// magenta signal — comms lamp
pink: [
"#f9ecf1",
"#f1d1dd",
"#e6adc2",
"#d885a2",
"#c8617f",
"#b44563",
"#9a3852",
"#7d2d42",
"#622334",
"#481a26",
],
// tracer chartreuse — tape-motion lamp
lime: [
"#f5f8de",
"#e9efad",
"#d9e378",
"#c5d24c",
"#adbb2f",
"#96a11f",
"#7c8618",
"#656e13",
"#4f560f",
"#3a3f0a",
],
// warm putty — light-scheme neutrals, seams, borders
gray: [
"#faf7f0",
"#f2ecdf",
"#e6ddc9",
"#d4c8ab",
"#bcac8b",
"#a08e6e",
"#837259",
"#665946",
"#4a4132",
"#302a20",
],
// powered-down deck — dark-scheme neutrals
dark: [
"#e9e3d8",
"#cfc6b6",
"#a89c88",
"#7c705f",
"#574c3f",
"#3a3128",
"#2a251f",
"#211e19",
"#191712",
"#100f0b",
],
} as const;
const manifest: ThemeManifest = {
id: "cassette",
label: msg`Cassette Futurism`,
colorSchemes: "both",
fontLinks: [
"https://fonts.googleapis.com/css2?family=Jost:wght@400;500;600;700&family=Space+Mono:wght@400;700&display=swap",
],
chromeColors: {
light: { base: "#ece4d4", dimmed: "#d4c8ab" },
dark: { base: "#211e19", dimmed: "#191712" },
},
mantine: {
defaultRadius: "md",
// Amber/lime/mustard fills are mid-luminance; without autoContrast they
// render white-on-amber at ~2-3:1.
autoContrast: true,
black: "#241f19",
primaryShade: { light: 6, dark: 5 },
colors: cassetteColors,
},
};
export default manifest;
+550
View File
@@ -0,0 +1,550 @@
/* Cassette Futurism — 1970s NASA/Braun control-deck instrument panel.
Light: warm beige enclosure with recessed panel seams and rounded-rect
modules in varied beige tones; buttons read as physical keys (top bevel,
1px darker base edge, press sinks 1px). Dark: powered-down deck with
glowing indicator lamps. Ramps/autoContrast live in manifest.ts; this
file carries shape, seams, and the physical-key/readout language. */
/* ---------- 1. Tokens, any scheme — (0,2,0) ---------- */
[data-theme="cassette"][data-theme] {
--mantine-font-family: "Jost", ui-sans-serif, system-ui, sans-serif;
--mantine-font-family-headings: "Jost", ui-sans-serif, system-ui, sans-serif;
--mantine-font-family-monospace: "Space Mono", ui-monospace, "SFMono-Regular",
monospace;
--mantine-heading-font-weight: 600;
--mantine-radius-xs: 6px;
--mantine-radius-sm: 8px;
--mantine-radius-md: 10px;
--mantine-radius-lg: 14px;
--mantine-radius-xl: 20px;
--mantine-radius-default: 10px;
--flxn-avatar-radius: 14px;
--flxn-avatar-border: 2px solid var(--mantine-color-default-border);
--flxn-badge-radius: 8px;
/* uppercase micro-label treatment shared by controls (buttons, badges,
tabs, toggles) — the "labeled toggle" panel aesthetic */
--cassette-label-font: "Space Mono", ui-monospace, monospace;
--cassette-label-tracking: 0.045em;
/* fine enclosure grain, tiled, baked-in low opacity */
--cassette-grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.035'/%3E%3C/svg%3E");
}
/* ---------- 2. Per-scheme tokens — (0,3,0) ---------- */
[data-theme="cassette"][data-theme][data-mantine-color-scheme="light"] {
--mantine-color-body: #ece4d4;
--mantine-color-text: #2c2620;
--mantine-color-bright: #1b1712;
--mantine-color-dimmed: #6b6255;
--mantine-color-placeholder: #857b6c;
--mantine-color-anchor: var(--mantine-primary-color-filled);
--mantine-color-error: #a43f2d;
--mantine-color-default: #f1ebdd;
--mantine-color-default-hover: #e4dcc8;
--mantine-color-default-color: #2c2620;
--mantine-color-default-border: #b9ad95;
--cassette-module: #f1ebdd;
--cassette-module-alt: #e7dfc9;
--cassette-seam-hi: rgba(255, 255, 255, 0.6);
--cassette-seam-lo: rgba(44, 38, 30, 0.28);
--cassette-well: rgba(44, 38, 30, 0.14);
--mantine-shadow-xs: inset 0 1px 0 var(--cassette-seam-hi),
0 1px 2px rgba(44, 38, 30, 0.18);
--mantine-shadow-sm: inset 0 1px 0 var(--cassette-seam-hi),
0 1px 2px rgba(44, 38, 30, 0.2), 0 2px 6px rgba(44, 38, 30, 0.12);
--mantine-shadow-md: inset 0 1px 0 var(--cassette-seam-hi),
0 3px 8px rgba(44, 38, 30, 0.18);
--mantine-shadow-lg: inset 0 1px 0 var(--cassette-seam-hi),
0 6px 16px rgba(44, 38, 30, 0.2);
--mantine-shadow-xl: inset 0 1px 0 var(--cassette-seam-hi),
0 10px 28px rgba(44, 38, 30, 0.24);
--flxn-gold: #ac7c19;
--flxn-silver: #837259;
--flxn-bronze: #9e4b1b;
--flxn-toast-success: #587a34;
--flxn-toast-error: #a43f2d;
--flxn-podium-1-bg: color-mix(in srgb, var(--mantine-color-yellow-5) 16%, #f6f1e6);
--flxn-podium-1-border: var(--mantine-color-yellow-7);
--flxn-podium-1-label: var(--mantine-color-yellow-9);
--flxn-podium-2-bg: color-mix(in srgb, var(--mantine-color-gray-5) 16%, #f6f1e6);
--flxn-podium-2-border: var(--mantine-color-gray-6);
--flxn-podium-2-label: var(--mantine-color-gray-8);
--flxn-podium-3-bg: color-mix(in srgb, var(--mantine-color-orange-5) 14%, #f6f1e6);
--flxn-podium-3-border: var(--mantine-color-orange-7);
--flxn-podium-3-label: var(--mantine-color-orange-9);
--flxn-showcase-bg: #f6f1e6;
--flxn-overlay: rgba(33, 28, 22, 0.45);
--flxn-dotgrid-color: #cabf9f;
--flxn-pop-shadow: rgba(44, 38, 30, 0.3);
}
[data-theme="cassette"][data-theme][data-mantine-color-scheme="dark"] {
--mantine-color-body: #211e19;
--mantine-color-text: #d8d0c0;
--mantine-color-bright: #f2ece0;
--mantine-color-dimmed: #8f8574;
--mantine-color-placeholder: #6f6656;
--mantine-color-anchor: var(--mantine-primary-color-light-color);
--mantine-color-error: #e2685a;
--mantine-color-default: #2a251f;
--mantine-color-default-hover: #332d25;
--mantine-color-default-color: #d8d0c0;
--mantine-color-default-border: #4a4335;
--cassette-module: #2a251f;
--cassette-module-alt: #241f19;
--cassette-seam-hi: rgba(255, 255, 255, 0.07);
--cassette-seam-lo: rgba(0, 0, 0, 0.55);
--cassette-well: rgba(0, 0, 0, 0.5);
--mantine-shadow-xs: inset 0 1px 0 var(--cassette-seam-hi),
0 1px 2px rgba(0, 0, 0, 0.5);
--mantine-shadow-sm: inset 0 1px 0 var(--cassette-seam-hi),
0 2px 6px rgba(0, 0, 0, 0.55);
--mantine-shadow-md: inset 0 1px 0 var(--cassette-seam-hi),
0 4px 12px rgba(0, 0, 0, 0.55);
--mantine-shadow-lg: inset 0 1px 0 var(--cassette-seam-hi),
0 8px 20px rgba(0, 0, 0, 0.6);
--mantine-shadow-xl: inset 0 1px 0 var(--cassette-seam-hi),
0 14px 32px rgba(0, 0, 0, 0.65);
--flxn-gold: #e0ac2c;
--flxn-silver: #a89c88;
--flxn-bronze: #d47237;
--flxn-toast-success: #86ac56;
--flxn-toast-error: #e2685a;
--flxn-podium-1-bg: color-mix(in srgb, var(--mantine-color-yellow-7) 26%, #241f19);
--flxn-podium-1-border: var(--mantine-color-yellow-5);
--flxn-podium-1-label: var(--mantine-color-yellow-3);
--flxn-podium-2-bg: color-mix(in srgb, var(--mantine-color-gray-6) 20%, #241f19);
--flxn-podium-2-border: var(--mantine-color-gray-4);
--flxn-podium-2-label: var(--mantine-color-gray-2);
--flxn-podium-3-bg: color-mix(in srgb, var(--mantine-color-orange-7) 26%, #241f19);
--flxn-podium-3-border: var(--mantine-color-orange-5);
--flxn-podium-3-label: var(--mantine-color-orange-3);
--flxn-showcase-bg: #241f19;
--flxn-overlay: rgba(0, 0, 0, 0.6);
--flxn-dotgrid-color: #3a352c;
--flxn-pop-shadow: rgba(0, 0, 0, 0.55);
}
/* ---------- 3. Enclosure texture: seams + grain (1 fixed overlay) ---------- */
[data-theme="cassette"] body {
background-color: var(--mantine-color-body);
background-image: var(--cassette-grain);
}
/* ---------- 4. AppShell: top bezel + seam rules ---------- */
[data-theme="cassette"] .mantine-AppShell-header {
background-color: var(--cassette-module);
border-bottom: 1px solid var(--mantine-color-default-border);
box-shadow: inset 0 -1px 0 var(--cassette-seam-hi),
0 2px 6px rgba(0, 0, 0, 0.12);
}
[data-theme="cassette"] .mantine-AppShell-header .mantine-Title-root {
font-family: var(--cassette-label-font);
text-transform: uppercase;
letter-spacing: var(--cassette-label-tracking);
}
[data-theme="cassette"] .mantine-AppShell-navbar {
background-color: var(--cassette-module);
border-inline-end: 1px solid var(--mantine-color-default-border);
}
[data-theme="cassette"] nav[role="navigation"].mantine-Paper-root {
background-color: var(--cassette-module);
border: 1px solid var(--mantine-color-default-border);
box-shadow: inset 0 1px 0 var(--cassette-seam-hi), var(--mantine-shadow-md);
}
/* ---------- 5. Buttons & ActionIcons: physical keys ---------- */
[data-theme="cassette"] .mantine-Button-root,
[data-theme="cassette"] .mantine-ActionIcon-root {
border-radius: var(--mantine-radius-md);
border: 1px solid var(--mantine-color-default-border);
font-family: var(--cassette-label-font);
font-weight: 700;
text-transform: uppercase;
letter-spacing: var(--cassette-label-tracking);
transition: transform 100ms ease, box-shadow 100ms ease;
}
[data-theme="cassette"] .mantine-Button-root[data-variant="default"],
[data-theme="cassette"] .mantine-ActionIcon-root[data-variant="default"] {
background-color: var(--cassette-module);
border-bottom: 3px solid color-mix(in srgb, var(--mantine-color-default-border) 70%, var(--mantine-color-bright));
box-shadow: inset 0 1px 0 var(--cassette-seam-hi);
color: var(--mantine-color-text);
}
[data-theme="cassette"] .mantine-Button-root[data-variant="filled"],
[data-theme="cassette"] .mantine-ActionIcon-root[data-variant="filled"],
[data-theme="cassette"] .mantine-Button-root:not([data-variant]),
[data-theme="cassette"] .mantine-ActionIcon-root:not([data-variant]) {
background-color: var(--mantine-primary-color-filled);
border-color: color-mix(in srgb, var(--mantine-primary-color-filled) 55%, black);
border-bottom: 3px solid color-mix(in srgb, var(--mantine-primary-color-filled) 45%, black);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3);
color: var(--mantine-primary-color-contrast);
}
[data-theme="cassette"][data-mantine-color-scheme="dark"] .mantine-Button-root[data-variant="filled"],
[data-theme="cassette"][data-mantine-color-scheme="dark"] .mantine-ActionIcon-root[data-variant="filled"],
[data-theme="cassette"][data-mantine-color-scheme="dark"] .mantine-Button-root:not([data-variant]),
[data-theme="cassette"][data-mantine-color-scheme="dark"] .mantine-ActionIcon-root:not([data-variant]) {
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),
0 0 8px color-mix(in srgb, var(--mantine-primary-color-filled) 55%, transparent);
}
[data-theme="cassette"] .mantine-Button-root[data-variant="light"],
[data-theme="cassette"] .mantine-ActionIcon-root[data-variant="light"] {
border-color: color-mix(in srgb, var(--mantine-primary-color-filled) 40%, transparent);
}
[data-theme="cassette"] .mantine-Button-root[data-variant="subtle"],
[data-theme="cassette"] .mantine-ActionIcon-root[data-variant="subtle"],
[data-theme="cassette"] .mantine-Button-root[data-variant="transparent"],
[data-theme="cassette"] .mantine-ActionIcon-root[data-variant="transparent"] {
border-color: transparent;
border-bottom-width: 1px;
}
/* pressed state — static sink cue, always on (no motion required) */
[data-theme="cassette"] .mantine-Button-root:active:not(:disabled):not([data-disabled]):not([data-loading]),
[data-theme="cassette"] .mantine-ActionIcon-root:active:not(:disabled):not([data-disabled]):not([data-loading]) {
border-bottom-width: 1px;
box-shadow: inset 0 2px 4px var(--cassette-well);
}
[data-theme="cassette"] .mantine-Button-root:disabled,
[data-theme="cassette"] .mantine-Button-root[data-disabled],
[data-theme="cassette"] .mantine-ActionIcon-root:disabled,
[data-theme="cassette"] .mantine-ActionIcon-root[data-disabled] {
box-shadow: none;
border-bottom-width: 1px;
}
/* the actual 1px sink — guarded, motion-only */
@media (prefers-reduced-motion: no-preference) {
[data-theme="cassette"] .flxn-press:active:not(:disabled):not([data-disabled]):not([data-loading]) {
transform: translateY(1px);
}
}
/* ---------- 6. Inputs: recessed readout wells ---------- */
[data-theme="cassette"] .mantine-Input-input {
background-color: var(--cassette-module-alt);
border: 1px solid var(--mantine-color-default-border);
border-radius: var(--mantine-radius-sm);
font-family: var(--cassette-label-font);
box-shadow: inset 0 1px 3px var(--cassette-well);
}
[data-theme="cassette"] .mantine-Input-input:focus,
[data-theme="cassette"] .mantine-Input-input:focus-within {
border-color: var(--mantine-primary-color-filled);
outline: none;
box-shadow: inset 0 1px 3px var(--cassette-well),
0 0 0 3px color-mix(in srgb, var(--mantine-primary-color-filled) 28%, transparent);
}
[data-theme="cassette"] .mantine-PinInput-field {
font-family: var(--cassette-label-font);
}
/* ---------- 7. Panels: Paper, Card, Modal, Popover, Notification, Alert ---------- */
[data-theme="cassette"] .mantine-Paper-root,
[data-theme="cassette"] .mantine-Card-root,
[data-theme="cassette"] .mantine-Popover-dropdown,
[data-theme="cassette"] .mantine-Modal-content,
[data-theme="cassette"] .mantine-Notification-root,
[data-theme="cassette"] .mantine-Alert-root {
background-color: var(--cassette-module);
border: 1px solid var(--mantine-color-default-border);
}
[data-theme="cassette"] .mantine-Paper-root,
[data-theme="cassette"] .mantine-Card-root {
box-shadow: var(--mantine-shadow-sm);
}
[data-theme="cassette"] .mantine-Modal-content {
box-shadow: var(--mantine-shadow-xl);
}
[data-theme="cassette"] .mantine-Modal-header {
background-color: var(--cassette-module-alt);
border-bottom: 1px solid var(--mantine-color-default-border);
box-shadow: inset 0 1px 0 var(--cassette-seam-hi);
}
[data-theme="cassette"] .mantine-Tooltip-tooltip {
background-color: var(--mantine-color-bright);
color: var(--mantine-color-body);
border: 1px solid var(--mantine-color-default-border);
font-family: var(--cassette-label-font);
font-size: 0.75rem;
}
/* ---------- 8. Tabs & SegmentedControl: labeled toggles ---------- */
[data-theme="cassette"] .mantine-Tabs-tab {
font-family: var(--cassette-label-font);
font-weight: 700;
text-transform: uppercase;
letter-spacing: var(--cassette-label-tracking);
}
[data-theme="cassette"] .mantine-Tabs-tab[data-active] {
border-color: var(--mantine-primary-color-filled);
color: var(--mantine-primary-color-filled);
}
[data-theme="cassette"] .mantine-SegmentedControl-root {
background-color: var(--cassette-module-alt);
border: 1px solid var(--mantine-color-default-border);
border-radius: var(--mantine-radius-md);
box-shadow: inset 0 1px 3px var(--cassette-well);
}
[data-theme="cassette"] .mantine-SegmentedControl-label {
font-family: var(--cassette-label-font);
text-transform: uppercase;
letter-spacing: var(--cassette-label-tracking);
font-size: 0.7rem;
}
[data-theme="cassette"] .mantine-SegmentedControl-indicator {
background-color: var(--cassette-module);
border: 1px solid var(--mantine-color-default-border);
border-radius: var(--mantine-radius-sm);
box-shadow: inset 0 1px 0 var(--cassette-seam-hi), 0 1px 2px var(--cassette-well);
}
/* ---------- 9. Badges, ThemeIcon, Checkbox/Radio, Switch ---------- */
[data-theme="cassette"] .mantine-Badge-root {
border: 1px solid var(--mantine-color-default-border);
border-radius: var(--mantine-radius-sm);
font-family: var(--cassette-label-font);
letter-spacing: var(--cassette-label-tracking);
}
[data-theme="cassette"] .mantine-ThemeIcon-root {
border: 1px solid var(--mantine-color-default-border);
}
[data-theme="cassette"] .mantine-Checkbox-input,
[data-theme="cassette"] .mantine-Radio-radio {
border: 1px solid var(--mantine-color-default-border);
box-shadow: inset 0 1px 2px var(--cassette-well);
}
/* checked-glyph on-color is hardcoded white by Mantine regardless of
autoContrast; recompute per accent so amber/lime checks stay legible */
[data-theme="cassette"] .mantine-Checkbox-root {
--checkbox-icon-color: var(--mantine-primary-color-contrast);
}
[data-theme="cassette"] .mantine-Radio-root {
--radio-icon-color: var(--mantine-primary-color-contrast);
}
[data-theme="cassette"] .mantine-Switch-track {
border: 1px solid var(--mantine-color-default-border);
box-shadow: inset 0 1px 3px var(--cassette-well);
}
[data-theme="cassette"] .mantine-Switch-thumb {
border: 1px solid var(--mantine-color-default-border);
box-shadow: inset 0 1px 0 var(--cassette-seam-hi), 0 1px 2px var(--cassette-well);
}
[data-theme="cassette"][data-mantine-color-scheme="dark"] .mantine-Switch-input:checked + .mantine-Switch-track {
box-shadow: inset 0 1px 3px var(--cassette-well),
0 0 6px color-mix(in srgb, var(--mantine-primary-color-filled) 60%, transparent);
}
/* ---------- 10. Progress: VU meter with tick marks ---------- */
[data-theme="cassette"] .mantine-Progress-root {
position: relative;
background-color: var(--cassette-module-alt);
border: 1px solid var(--mantine-color-default-border);
box-shadow: inset 0 1px 3px var(--cassette-well);
}
[data-theme="cassette"] .mantine-Progress-root::after {
content: "";
position: absolute;
inset: 0;
z-index: 1;
pointer-events: none;
background-image: repeating-linear-gradient(
90deg,
var(--mantine-color-default-border) 0 1px,
transparent 1px 12.5%
);
opacity: 0.6;
}
[data-theme="cassette"] .mantine-Progress-section {
background-color: var(--mantine-primary-color-filled);
background-image: linear-gradient(
180deg,
rgba(255, 255, 255, 0.32) 0%,
rgba(255, 255, 255, 0.05) 45%,
rgba(0, 0, 0, 0.1) 100%
);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
[data-theme="cassette"][data-mantine-color-scheme="dark"] .mantine-Progress-section {
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.16),
0 0 6px color-mix(in srgb, var(--mantine-primary-color-filled) 65%, transparent);
}
/* ---------- 11. Divider: tape-window (two reel circles + line) ---------- */
[data-theme="cassette"] .mantine-Divider-root {
position: relative;
--divider-color: var(--mantine-color-default-border);
}
[data-theme="cassette"] .mantine-Divider-root:not([data-orientation="vertical"])::before,
[data-theme="cassette"] .mantine-Divider-root:not([data-orientation="vertical"])::after {
content: "";
position: absolute;
top: 50%;
width: 8px;
height: 8px;
border-radius: 50%;
transform: translateY(-50%);
background-color: var(--cassette-module);
border: 1px solid var(--mantine-color-default-border);
box-shadow: inset 0 1px 1px var(--cassette-well), inset 0 -1px 0 var(--cassette-seam-hi);
pointer-events: none;
}
[data-theme="cassette"] .mantine-Divider-root:not([data-orientation="vertical"])::before {
left: 42%;
}
[data-theme="cassette"] .mantine-Divider-root:not([data-orientation="vertical"])::after {
left: 58%;
}
/* ---------- 12. App hooks: tab strip ---------- */
[data-theme="cassette"] .flxn-tabs[data-stuck] {
background-color: var(--cassette-module);
border-bottom-color: var(--mantine-color-default-border);
box-shadow: inset 0 -1px 0 var(--cassette-seam-hi);
}
[data-theme="cassette"] .flxn-tab {
font-family: var(--cassette-label-font);
font-weight: 700;
text-transform: uppercase;
letter-spacing: var(--cassette-label-tracking);
font-size: 0.75rem;
}
/* underline indicator (not a filled pill), so accent text stays legible */
[data-theme="cassette"] .flxn-tab[data-active]::before {
content: "";
display: inline-block;
width: 6px;
height: 6px;
margin-inline-end: 6px;
border-radius: 50%;
background-color: var(--mantine-primary-color-filled);
vertical-align: middle;
}
[data-theme="cassette"][data-mantine-color-scheme="dark"] .flxn-tab[data-active]::before {
box-shadow: 0 0 5px color-mix(in srgb, var(--mantine-primary-color-filled) 80%, transparent);
}
[data-theme="cassette"] .flxn-tabs-indicator {
border-bottom: 3px solid var(--mantine-primary-color-filled);
}
[data-theme="cassette"][data-mantine-color-scheme="dark"] .flxn-tabs-indicator {
box-shadow: none;
}
/* ---------- 13. App hooks: nav tiles ---------- */
[data-theme="cassette"] .flxn-tile {
background-color: var(--cassette-module);
border: 1px solid var(--mantine-color-default-border);
border-bottom: 3px solid color-mix(in srgb, var(--mantine-color-default-border) 70%, var(--mantine-color-bright));
box-shadow: inset 0 1px 0 var(--cassette-seam-hi);
transition: transform 100ms ease, box-shadow 100ms ease;
}
[data-theme="cassette"] .flxn-tile:active {
border-bottom-width: 1px;
box-shadow: inset 0 2px 4px var(--cassette-well);
}
@media (prefers-reduced-motion: no-preference) {
[data-theme="cassette"] .flxn-tile.flxn-press:active {
transform: translateY(1px);
}
}
/* flat tint — no plate behind the glyph */
[data-theme="cassette"] .flxn-tile-icon {
color: var(--mantine-primary-color-light-color);
}
/* ---------- 14. App hooks: see-all as a labeled key chip ---------- */
[data-theme="cassette"] .flxn-see-all {
border-radius: var(--mantine-radius-sm);
border: 1px solid color-mix(in srgb, var(--mantine-primary-color-filled) 45%, transparent);
background-color: var(--mantine-primary-color-light);
color: var(--mantine-primary-color-light-color);
font-family: var(--cassette-label-font);
font-weight: 700;
text-transform: uppercase;
letter-spacing: var(--cassette-label-tracking);
font-size: 0.7rem;
box-shadow: inset 0 1px 0 var(--cassette-seam-hi);
}
/* ---------- 15. App hooks: match card + reaction tray ---------- */
[data-theme="cassette"] .flxn-match-card {
background-color: var(--cassette-module);
border: 1px solid var(--mantine-color-default-border);
box-shadow: var(--mantine-shadow-sm);
}
[data-theme="cassette"] .flxn-reaction-row {
background-color: var(--cassette-module-alt);
border: 1px solid var(--mantine-color-default-border);
box-shadow: inset 0 2px 4px var(--cassette-well);
}
/* ---------- 16. App hooks: avatar frame (porthole bezel) ---------- */
[data-theme="cassette"] .flxn-avatar-frame {
box-shadow: inset 0 1px 0 var(--cassette-seam-hi), inset 0 -1px 2px var(--cassette-well);
}
+11
View File
@@ -0,0 +1,11 @@
import { CHROME_COLORS, ChromeColorScheme } from "@/lib/mantine/theme-colors";
import type { ThemeManifest } from "./types";
export function resolveChromeColors(
manifest: ThemeManifest
): Record<ChromeColorScheme, { base: string; dimmed: string }> {
return {
light: manifest.chromeColors?.light ?? CHROME_COLORS.light,
dark: manifest.chromeColors?.dark ?? CHROME_COLORS.dark,
};
}
+158
View File
@@ -0,0 +1,158 @@
import { msg } from "@lingui/core/macro";
import type { ThemeManifest } from "../types";
/* Carnival poster inks. Declared in JS so Mantine regenerates every derived
variant var (-light rgba, -outline, filled hovers) from the retuned shades. */
const circusColors = {
// royal
blue: [
"#eef2fc",
"#d8e1f8",
"#b6c8f0",
"#8fa9e6",
"#6787d8",
"#4267c8",
"#2b4fb4",
"#21409a",
"#19317a",
"#11235c",
],
// carnival red
red: [
"#fdefed",
"#f9d8d4",
"#f2b6af",
"#e88d84",
"#d9655c",
"#c6443c",
"#b12724",
"#931c1d",
"#731316",
"#540c10",
],
// ticket-booth emerald
green: [
"#ecf8ef",
"#d2eeda",
"#a9dfba",
"#7bcb95",
"#4fb573",
"#2f9c57",
"#1d7f41",
"#156634",
"#0f4e27",
"#09381c",
],
// mustard
yellow: [
"#fbf4e2",
"#f5e5b8",
"#edd288",
"#e0ba55",
"#cf9f2a",
"#b58211",
"#97660a",
"#7b5207",
"#5f3f05",
"#452d03",
],
// sideshow plum
grape: [
"#f6effc",
"#e8d8f6",
"#d3b6ee",
"#b98ee2",
"#9d66d2",
"#8547bd",
"#6f2da0",
"#5b2284",
"#461868",
"#33104d",
],
// pumpkin
orange: [
"#fdf1e8",
"#f9dcc6",
"#f3bd97",
"#e99a63",
"#dc7736",
"#cd6016",
"#b84d09",
"#973d06",
"#752f05",
"#542103",
],
// raspberry
pink: [
"#fcedf4",
"#f8d3e4",
"#f1abcb",
"#e67dac",
"#d6528d",
"#c43574",
"#ab1f60",
"#8d154e",
"#6e0e3c",
"#50082b",
],
// fairground olive
lime: [
"#f5f8e4",
"#e7efbe",
"#d3e18d",
"#bccd5c",
"#a2b532",
"#849a15",
"#667c07",
"#536505",
"#404e04",
"#2e3902",
],
// warm parchment neutrals — light-scheme grays
gray: [
"#faf5ea",
"#f1e8d6",
"#e5d8bf",
"#d3c1a2",
"#b8a284",
"#97826a",
"#6f5d4b",
"#554638",
"#3d3128",
"#28201a",
],
// night fair — dark-scheme surfaces/borders
dark: [
"#f2e6da",
"#d8c7bb",
"#b5a096",
"#8a7268",
"#5c4547",
"#47343a",
"#382930",
"#2a1e24",
"#20161b",
"#150e12",
],
} as const;
const manifest: ThemeManifest = {
id: "circus",
label: msg`Big Top`,
colorSchemes: "both",
fontLinks: [
"https://fonts.googleapis.com/css2?family=Alfa+Slab+One&display=swap",
"https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500&display=swap",
],
chromeColors: {
light: { base: "#f9f1e0", dimmed: "#e9dcbd" },
dark: { base: "#1e151a", dimmed: "#140d11" },
},
mantine: {
// deep poster inks in both schemes keep white filled-text legible
primaryShade: { light: 6, dark: 6 },
colors: circusColors,
},
};
export default manifest;
+608
View File
@@ -0,0 +1,608 @@
/* Big Top — vintage carnival.
Light: cream ticket paper, awning-stripe bands, scalloped card edges.
Dark: night fair with warm glowing surfaces. */
/* ---------------------------------------------------------------- */
/* 1. Tokens, any scheme — (0,2,0) */
/* ---------------------------------------------------------------- */
[data-theme="circus"][data-theme] {
--mantine-font-family: "Rubik", "Trebuchet MS", sans-serif;
--mantine-font-family-headings: "Alfa Slab One", "Rockwell", "Georgia", serif;
--mantine-heading-font-weight: 400;
--mantine-h1-font-size: 2rem;
--mantine-h1-line-height: 1.15;
--mantine-h2-font-size: 1.6rem;
--mantine-h2-line-height: 1.2;
--mantine-h3-font-size: 1.3rem;
--mantine-h3-line-height: 1.25;
/* ticket-corner radii */
--mantine-radius-xs: 0.25rem;
--mantine-radius-sm: 0.375rem;
--mantine-radius-md: 0.5rem;
--mantine-radius-lg: 0.75rem;
--mantine-radius-xl: 1rem;
--mantine-radius-default: 0.5rem;
/* warm soft elevation (dark overrides below) */
--mantine-shadow-xs: 0 1px 2px rgba(64, 41, 29, 0.12);
--mantine-shadow-sm: 0 2px 6px rgba(64, 41, 29, 0.16);
--mantine-shadow-md: 0 4px 12px rgba(64, 41, 29, 0.2);
--mantine-shadow-lg: 0 8px 20px rgba(64, 41, 29, 0.22);
--mantine-shadow-xl: 0 12px 32px rgba(64, 41, 29, 0.26);
--flxn-avatar-radius: 50%;
--flxn-avatar-border: 2px solid var(--circus-ink);
--flxn-badge-radius: 0.5rem;
/* awning valance band (stripe colors set per scheme) */
--circus-awning: repeating-linear-gradient(
90deg,
var(--circus-red) 0 0.75rem,
var(--circus-stripe) 0.75rem 1.5rem
);
}
/* ---------------------------------------------------------------- */
/* 2. Light — cream ticket paper — (0,3,0) */
/* ---------------------------------------------------------------- */
[data-theme="circus"][data-theme][data-mantine-color-scheme="light"] {
--circus-ink: #40291d;
--circus-paper: #fffaef;
--circus-red: #b02e26;
--circus-stripe: #f6ead0;
--circus-scallop: #b02e26;
--circus-gold: #a8790f;
--mantine-color-body: #f9f1e0;
--mantine-color-text: #33221a;
--mantine-color-bright: #1d100a;
--mantine-color-dimmed: #7a6250;
--mantine-color-anchor: var(--mantine-primary-color-light-color);
--mantine-color-error: #b12724;
--mantine-color-placeholder: #8d7660;
--mantine-color-default: #fffaef;
--mantine-color-default-hover: #f3e7cb;
--mantine-color-default-color: #33221a;
--mantine-color-default-border: #40291d;
/* app tokens */
--flxn-gold: #c2900e;
--flxn-silver: #857c6e;
--flxn-bronze: #b05c2c;
--flxn-toast-success: #1d7f41;
--flxn-toast-error: #b12724;
--flxn-podium-1-bg: #f7e8b8;
--flxn-podium-1-border: #c2900e;
--flxn-podium-1-label: #6b4e08;
--flxn-podium-2-bg: #efe9dc;
--flxn-podium-2-border: #9a8f7c;
--flxn-podium-2-label: #5c5344;
--flxn-podium-3-bg: #f6dfc8;
--flxn-podium-3-border: #b05c2c;
--flxn-podium-3-label: #7c3c14;
--flxn-showcase-bg: #fffaef;
--flxn-overlay: rgba(38, 24, 16, 0.5);
--flxn-dotgrid-color: rgba(64, 41, 29, 0.3);
--flxn-pop-shadow: rgba(64, 41, 29, 0.35);
}
/* ---------------------------------------------------------------- */
/* 2. Dark — night fair — (0,3,0) */
/* ---------------------------------------------------------------- */
[data-theme="circus"][data-theme][data-mantine-color-scheme="dark"] {
--circus-ink: #6b4f43;
--circus-paper: #2a1e24;
--circus-red: #a03530;
--circus-stripe: #d8b073;
--circus-scallop: #a03530;
--circus-gold: #e3b45c;
--mantine-color-body: #1e151a;
--mantine-color-text: #f4e6d4;
--mantine-color-bright: #fff6e8;
--mantine-color-dimmed: #b39d8c;
--mantine-color-anchor: var(--mantine-primary-color-light-color);
--mantine-color-error: #f2857e;
--mantine-color-placeholder: #8a7466;
--mantine-color-default: #2a1e24;
--mantine-color-default-hover: #362730;
--mantine-color-default-color: #f4e6d4;
--mantine-color-default-border: #5c443c;
/* black depth plus a faint fairground-lamp glow */
--mantine-shadow-xs: 0 1px 3px rgba(0, 0, 0, 0.4);
--mantine-shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.45),
0 0 14px rgba(232, 178, 102, 0.06);
--mantine-shadow-md: 0 4px 14px rgba(0, 0, 0, 0.5),
0 0 18px rgba(232, 178, 102, 0.08);
--mantine-shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.55),
0 0 24px rgba(232, 178, 102, 0.09);
--mantine-shadow-xl: 0 14px 36px rgba(0, 0, 0, 0.6),
0 0 32px rgba(232, 178, 102, 0.1);
/* app tokens */
--flxn-gold: #e8b84f;
--flxn-silver: #c9bfae;
--flxn-bronze: #e09060;
--flxn-toast-success: #6fce8f;
--flxn-toast-error: #f2857e;
--flxn-podium-1-bg: #322818;
--flxn-podium-1-border: #9c7c2c;
--flxn-podium-1-label: #ecc86a;
--flxn-podium-2-bg: #2c2522;
--flxn-podium-2-border: #6e6357;
--flxn-podium-2-label: #d6cabb;
--flxn-podium-3-bg: #34211a;
--flxn-podium-3-border: #9c5b34;
--flxn-podium-3-label: #eda579;
--flxn-showcase-bg: #2a1e24;
--flxn-overlay: rgba(8, 4, 6, 0.65);
--flxn-dotgrid-color: rgba(240, 221, 196, 0.22);
--flxn-pop-shadow: rgba(0, 0, 0, 0.6);
}
/* ---------------------------------------------------------------- */
/* 3. Type — slab display headings, sparingly */
/* ---------------------------------------------------------------- */
[data-theme="circus"] :is(h1, h2, h3) {
letter-spacing: 0.035em;
}
/* small headings stay in the body face so the slab reads as a poster
headline, not a wall of it */
[data-theme="circus"] :is(h4, h5, h6) {
font-family: var(--mantine-font-family);
font-weight: 700;
letter-spacing: 0.01em;
}
/* ---------------------------------------------------------------- */
/* 4. Surfaces — warm-ink borders, scalloped card valances */
/* ---------------------------------------------------------------- */
[data-theme="circus"] .mantine-Paper-root {
background-color: var(--circus-paper);
border: 2px solid var(--circus-ink);
box-shadow: var(--mantine-shadow-sm);
}
/* scallop strip painted along the top edge only (repeat-x half-discs);
bracket cards are dense and small — no valance there */
[data-theme="circus"] .mantine-Card-root:not(.flxn-bracket-card),
[data-theme="circus"] .flxn-match-card.flxn-match-card {
background-image: radial-gradient(
circle at 0.375rem 0,
var(--circus-scallop) 0.3rem,
transparent calc(0.3rem + 1px)
);
background-repeat: repeat-x;
background-size: 0.75rem 0.45rem;
background-position: top left;
}
/* ---------------------------------------------------------------- */
/* 5. Buttons & action icons — poster lettering on inked plates */
/* ---------------------------------------------------------------- */
[data-theme="circus"] .mantine-Button-root {
border: 2px solid var(--circus-ink);
border-radius: var(--mantine-radius-sm);
box-shadow: 0 2px 0 color-mix(in srgb, var(--circus-ink) 45%, transparent);
background-clip: padding-box;
}
[data-theme="circus"] .mantine-Button-label {
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
}
[data-theme="circus"]
.mantine-Button-root:is(
[data-variant="subtle"],
[data-variant="transparent"],
[data-variant="white"]
) {
border: none;
box-shadow: none;
}
[data-theme="circus"]
.mantine-ActionIcon-root:is(
[data-variant="filled"],
[data-variant="default"],
[data-variant="outline"],
[data-variant="light"]
) {
border: 2px solid var(--circus-ink);
box-shadow: var(--mantine-shadow-xs);
}
@media (prefers-reduced-motion: no-preference) {
[data-theme="circus"] .mantine-Button-root {
transition:
transform 140ms cubic-bezier(0.32, 0.72, 0, 1),
box-shadow 140ms cubic-bezier(0.32, 0.72, 0, 1);
}
[data-theme="circus"]
.mantine-Button-root:not(
[data-variant="subtle"],
[data-variant="transparent"],
[data-variant="white"]
):active:not([data-disabled]):not(:disabled) {
transform: translateY(1px);
box-shadow: 0 1px 0 color-mix(in srgb, var(--circus-ink) 45%, transparent);
}
}
/* ---------------------------------------------------------------- */
/* 6. Inputs — ticket-window frames */
/* ---------------------------------------------------------------- */
[data-theme="circus"] .mantine-Input-input {
background-color: var(--circus-paper);
border: 2px solid var(--circus-ink);
border-radius: var(--mantine-radius-sm);
}
[data-theme="circus"] .mantine-Input-input:focus,
[data-theme="circus"] .mantine-Input-input:focus-within {
border-color: var(--mantine-primary-color-filled);
box-shadow: 0 0 0 3px
color-mix(in srgb, var(--mantine-primary-color-filled) 25%, transparent);
}
[data-theme="circus"] .mantine-InputWrapper-label {
font-weight: 600;
letter-spacing: 0.01em;
}
[data-theme="circus"] .mantine-PinInput-field {
border: 2px solid var(--circus-ink);
border-radius: var(--mantine-radius-sm);
font-weight: 700;
}
/* ---------------------------------------------------------------- */
/* 7. Badges — prize ribbons (notched banner ends) */
/* ---------------------------------------------------------------- */
[data-theme="circus"] .mantine-Badge-root {
border-radius: 0;
clip-path: polygon(
0 0,
100% 0,
calc(100% - 0.45em) 50%,
100% 100%,
0 100%,
0.45em 50%
);
padding-inline: 0.95em;
font-weight: 700;
letter-spacing: 0.05em;
}
/* ---------------------------------------------------------------- */
/* 8. Tabs & SegmentedControl */
/* ---------------------------------------------------------------- */
[data-theme="circus"] .mantine-Tabs-list::before {
border-color: color-mix(in srgb, var(--circus-ink) 40%, transparent);
}
[data-theme="circus"] .mantine-Tabs-tab {
font-weight: 600;
}
[data-theme="circus"] .mantine-Tabs-tab[data-active] {
border-color: var(--mantine-primary-color-filled);
color: var(--mantine-primary-color-light-color);
}
[data-theme="circus"] .mantine-SegmentedControl-root {
background-color: var(--circus-paper);
border: 2px solid var(--circus-ink);
border-radius: var(--mantine-radius-sm);
}
[data-theme="circus"] .mantine-SegmentedControl-indicator {
background-color: var(--mantine-primary-color-filled);
border-radius: var(--mantine-radius-xs);
box-shadow: none;
}
[data-theme="circus"] .mantine-SegmentedControl-label {
font-weight: 600;
}
[data-theme="circus"] .mantine-SegmentedControl-label[data-active] {
color: var(--mantine-primary-color-contrast);
}
/* ---------------------------------------------------------------- */
/* 9. Overlays — marquee-framed modal, poster dropdowns */
/* ---------------------------------------------------------------- */
[data-theme="circus"] .mantine-Modal-content {
background-color: var(--circus-paper);
border: 2px solid var(--circus-ink);
border-radius: var(--mantine-radius-md);
box-shadow: var(--mantine-shadow-xl);
/* marquee-bulb ring just inside the frame */
outline: 2px dotted var(--circus-gold);
outline-offset: -0.45rem;
}
[data-theme="circus"] .mantine-Modal-header {
background-color: var(--circus-paper);
border-bottom: 2px solid var(--circus-ink);
}
[data-theme="circus"] .mantine-Modal-title {
font-family: var(--mantine-font-family-headings);
font-weight: 400;
font-size: 1.25rem;
letter-spacing: 0.035em;
}
[data-theme="circus"] .mantine-Popover-dropdown,
[data-theme="circus"] .mantine-Menu-dropdown {
background-color: var(--circus-paper);
border: 2px solid var(--circus-ink);
border-radius: var(--mantine-radius-sm);
box-shadow: var(--mantine-shadow-md);
}
/* sign plate: inverts per scheme, always warm-on-warm neutral */
[data-theme="circus"] .mantine-Tooltip-tooltip {
border-radius: var(--mantine-radius-sm);
font-weight: 600;
}
[data-theme="circus"][data-mantine-color-scheme="light"] .mantine-Tooltip-tooltip {
background-color: #33221a;
color: #f9f1e0;
}
[data-theme="circus"][data-mantine-color-scheme="dark"] .mantine-Tooltip-tooltip {
background-color: #f4e6d4;
color: #33221a;
}
[data-theme="circus"] .mantine-Notification-root {
background-color: var(--circus-paper);
border: 2px solid var(--circus-ink);
border-radius: var(--mantine-radius-md);
box-shadow: var(--mantine-shadow-lg);
}
[data-theme="circus"] .mantine-Notification-title {
font-weight: 700;
letter-spacing: 0.02em;
}
[data-theme="circus"] .mantine-Alert-root {
border: 2px solid var(--circus-ink);
border-radius: var(--mantine-radius-md);
}
[data-theme="circus"] .mantine-Alert-title {
font-weight: 700;
letter-spacing: 0.02em;
}
/* ---------------------------------------------------------------- */
/* 10. App shell — awning band under the header */
/* ---------------------------------------------------------------- */
[data-theme="circus"] .mantine-AppShell-root {
--app-shell-border-color: var(--circus-ink);
}
[data-theme="circus"] .mantine-AppShell-header {
background-color: var(--circus-paper);
background-image: var(--circus-awning);
background-repeat: no-repeat;
background-size: 100% 0.5rem;
background-position: bottom left;
border-bottom: 2px solid var(--circus-ink);
}
[data-theme="circus"] .mantine-AppShell-navbar {
background-color: var(--circus-paper);
border-inline-end: 2px solid var(--circus-ink);
}
[data-theme="circus"] .mantine-AppShell-aside {
border-inline-start: 2px solid var(--circus-ink);
}
/* ---------------------------------------------------------------- */
/* 11. Small controls & details */
/* ---------------------------------------------------------------- */
[data-theme="circus"] .mantine-Checkbox-input {
background-color: var(--circus-paper);
border: 2px solid var(--circus-ink);
border-radius: var(--mantine-radius-xs);
}
[data-theme="circus"] .mantine-Checkbox-input:checked {
background-color: var(--mantine-primary-color-filled);
border-color: var(--circus-ink);
}
[data-theme="circus"] .mantine-Switch-track {
background-color: var(--circus-paper);
border: 2px solid var(--circus-ink);
}
[data-theme="circus"] .mantine-Switch-input:checked + .mantine-Switch-track {
background-color: var(--mantine-primary-color-filled);
border-color: var(--circus-ink);
}
[data-theme="circus"] .mantine-Switch-thumb {
background-color: #fffaef;
border: 2px solid var(--circus-ink);
}
/* striped midway banner bar */
[data-theme="circus"] .mantine-Progress-root {
background-color: var(--circus-paper);
border: 2px solid var(--circus-ink);
border-radius: var(--mantine-radius-xl);
}
[data-theme="circus"] .mantine-Progress-section {
border-radius: var(--mantine-radius-xl);
background-image: repeating-linear-gradient(
45deg,
rgba(255, 250, 239, 0.28) 0 0.5rem,
transparent 0.5rem 1rem
);
}
[data-theme="circus"] .mantine-ThemeIcon-root {
border: 2px solid var(--circus-ink);
}
[data-theme="circus"] .mantine-Indicator-indicator {
border: 2px solid var(--circus-paper);
font-weight: 700;
}
[data-theme="circus"] .mantine-NavLink-root {
font-weight: 600;
border-radius: var(--mantine-radius-sm);
}
[data-theme="circus"] .mantine-Divider-root {
--divider-color: color-mix(in srgb, var(--circus-ink) 45%, transparent);
}
[data-theme="circus"] .mantine-Skeleton-root {
border-radius: var(--mantine-radius-sm);
}
/* ---------------------------------------------------------------- */
/* 12. App component hooks */
/* ---------------------------------------------------------------- */
/* Tab strip: flat resting; docking drops the awning valance. Rule width
stays 2px in both states so pinning never shifts layout. */
[data-theme="circus"] .flxn-tabs {
border-bottom-width: 2px;
}
[data-theme="circus"] .flxn-tabs[data-stuck] {
background-color: var(--circus-paper);
background-image: var(--circus-awning);
background-repeat: no-repeat;
background-size: 100% 0.375rem;
background-position: bottom left;
border-bottom-color: var(--circus-ink);
}
[data-theme="circus"] .flxn-tab.flxn-tab {
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
}
[data-theme="circus"] .flxn-tab.flxn-tab[data-active] {
color: var(--mantine-primary-color-light-color);
font-weight: 700;
}
/* marquee-dot indicator: a dotted rule, so accent tab text is fine */
[data-theme="circus"] .flxn-tabs-indicator.flxn-tabs-indicator {
border-bottom: 0.25rem dotted var(--mantine-primary-color-filled);
padding-inline: 0.4rem;
}
/* Nav grid tiles: sideshow plates with flat accent-tinted icons */
[data-theme="circus"] .flxn-tile {
background-color: var(--circus-paper);
border: 2px solid var(--circus-ink);
border-radius: var(--mantine-radius-md);
box-shadow: var(--mantine-shadow-xs);
}
@media (hover: hover) {
[data-theme="circus"] .flxn-tile:hover:not(:disabled) {
background-color: color-mix(
in srgb,
var(--mantine-primary-color-filled) 8%,
var(--circus-paper)
);
}
}
[data-theme="circus"] .flxn-tile:active:not(:disabled) {
background-color: color-mix(
in srgb,
var(--mantine-primary-color-filled) 8%,
var(--circus-paper)
);
}
[data-theme="circus"] .flxn-tile-icon {
color: var(--mantine-primary-color-light-color);
}
/* "See all" as a ticket stub: accent wash + dashed perforation edge */
[data-theme="circus"] .flxn-see-all.flxn-see-all {
font-weight: 700;
letter-spacing: 0.02em;
background-color: var(--mantine-primary-color-light);
color: var(--mantine-primary-color-light-color);
border: 1px dashed
color-mix(in srgb, var(--mantine-primary-color-light-color) 70%, transparent);
border-radius: var(--mantine-radius-sm);
}
@media (hover: hover) {
[data-theme="circus"] .flxn-see-all.flxn-see-all:hover {
background-color: var(--mantine-primary-color-light-hover);
text-decoration: none;
}
}
/* Match card + tray: same 2px warm-ink frame; tray one shade deeper */
[data-theme="circus"] .flxn-match-card.flxn-match-card {
background-color: var(--circus-paper);
border: 2px solid var(--circus-ink);
box-shadow: var(--mantine-shadow-sm);
}
[data-theme="circus"] .flxn-reaction-row.flxn-reaction-row {
background-color: color-mix(in srgb, var(--circus-ink) 8%, var(--circus-paper));
border: 2px solid var(--circus-ink);
border-radius: var(--mantine-radius-md);
box-shadow: var(--mantine-shadow-xs);
}
/* ---------------------------------------------------------------- */
/* 13. Accent-driven selection & focus */
/* ---------------------------------------------------------------- */
[data-theme="circus"] ::selection {
background-color: color-mix(
in srgb,
var(--mantine-primary-color-filled) 30%,
transparent
);
}
[data-theme="circus"] :focus-visible {
outline: 2px solid var(--mantine-primary-color-filled);
outline-offset: 2px;
}
+165
View File
@@ -0,0 +1,165 @@
import { msg } from "@lingui/core/macro";
import type { ThemeManifest } from "../types";
/* Plasticine ramps — chalky, high-pigment clay mixes. Declared in JS so
Mantine regenerates every derived variant var (-light rgba, -outline,
autoContrast picks) from the retuned shades. Shade 4 (the fill) keeps
>=4.5:1 with the clay ink #33291f; shade 8 keeps >=4.5:1 on the light
backdrop; shades 3-4 keep >=4.5:1 on the dark backdrop. */
const clayColors = {
// denim clay
blue: [
"#eef3f7",
"#dbe7f0",
"#c2d7e8",
"#9cc0dc",
"#6fa5cd",
"#5590bd",
"#4478a3",
"#396286",
"#2d4d69",
"#22394e",
],
// terracotta
red: [
"#f9eeea",
"#f2dcd5",
"#ecc5b9",
"#e8a893",
"#e08466",
"#cd6a4c",
"#b0543a",
"#8f4430",
"#713627",
"#54291e",
],
// moss clay
green: [
"#edf4ea",
"#dcead5",
"#c4dcb9",
"#a3c996",
"#7fb371",
"#689e5b",
"#538148",
"#44693c",
"#365330",
"#283d24",
],
// mustard clay
yellow: [
"#faf4e2",
"#f5e8c2",
"#efd99b",
"#e9c96f",
"#e0b545",
"#c99b2e",
"#a67d24",
"#86641e",
"#684d18",
"#4b3812",
],
// plum clay
grape: [
"#f4eef6",
"#e8dcec",
"#d9c4e0",
"#c5a4d2",
"#b28ac3",
"#9c70ae",
"#7c548d",
"#654573",
"#4f365a",
"#3a2842",
],
// apricot clay
orange: [
"#faf0e6",
"#f4dfc9",
"#eecaa6",
"#e8b17e",
"#de9355",
"#c97a3d",
"#a86230",
"#885028",
"#6a3f20",
"#4d2e18",
],
// rose clay
pink: [
"#f9eef1",
"#f2dbe2",
"#ecc2cf",
"#e6a1b6",
"#dd7f9c",
"#c96585",
"#ab4f6d",
"#8b4059",
"#6d3346",
"#502634",
],
// pea clay
lime: [
"#f3f6e4",
"#e7edc6",
"#d6e19f",
"#c1d174",
"#a8bd4c",
"#90a636",
"#75882b",
"#5f6e24",
"#4a561d",
"#363f16",
],
// warm putty — light-scheme neutrals, borders, dimmed text
gray: [
"#f7f3ec",
"#ede7dd",
"#e0d8cb",
"#cec3b2",
"#b5a795",
"#97887a",
"#786a5d",
"#5d5248",
"#453d36",
"#2e2925",
],
// night studio — dark-scheme body/surfaces/borders, warm-tinted
dark: [
"#e8e0da",
"#cfc4bd",
"#b1a49e",
"#857a78",
"#56494e",
"#453a3f",
"#332c30",
"#262023",
"#1e191c",
"#141013",
],
} as const;
const manifest: ThemeManifest = {
id: "clay",
label: msg`Claymation`,
colorSchemes: "both",
fontLinks: [
"https://fonts.googleapis.com/css2?family=Baloo+2:wght@500;600;700;800&display=swap",
"https://fonts.googleapis.com/css2?family=Nunito:wght@400;500;600;700;800&display=swap",
],
chromeColors: {
light: { base: "#efe9e2", dimmed: "#d8cfc2" },
dark: { base: "#262023", dimmed: "#141013" },
},
mantine: {
defaultRadius: "md",
// Chalky mid-tone fills pair with dark clay ink, not white
autoContrast: true,
luminanceThreshold: 0.28,
black: "#33291f",
primaryShade: { light: 4, dark: 4 },
colors: clayColors,
},
};
export default manifest;
+620
View File
@@ -0,0 +1,620 @@
/* Claymation — hand-pinched plasticine world.
Light: soft studio backdrop; surfaces are matte clay blobs with irregular
organic radii, "pinched" depth from an inner top highlight plus a soft
bottom shadow. No hard edges anywhere.
Dark: night studio with a warm key light — highlights warmer, shadows
deeper. Filled controls carry dark clay ink, never white-on-mid-tone. */
/* ---------- 1. Tokens, any scheme ---------- */
[data-theme="clay"][data-theme] {
--mantine-font-family: "Nunito", "Inter", system-ui, sans-serif;
--mantine-font-family-headings: "Baloo 2", "Nunito", system-ui, sans-serif;
--mantine-heading-font-weight: 700;
--mantine-radius-xs: 10px;
--mantine-radius-sm: 14px;
--mantine-radius-md: 18px;
--mantine-radius-lg: 24px;
--mantine-radius-xl: 30px;
--mantine-radius-default: 18px;
/* hand-pinched blob radii, varied per component */
--clay-blob-card: 26px 20px 28px 22px / 22px 28px 20px 26px;
--clay-blob-card-alt: 20px 28px 22px 26px / 28px 20px 26px 22px;
--clay-blob-md: 20px 16px 22px 18px / 18px 22px 16px 20px;
--clay-blob-btn: 16px 20px 14px 22px / 20px 14px 22px 16px;
--clay-blob-round: 42% 58% 55% 45% / 48% 45% 55% 52%;
--clay-blob-pellet: 14px 18px 15px 19px / 18px 14px 19px 15px;
--clay-ink: #33291f;
--flxn-avatar-radius: 45%;
--flxn-avatar-border: 3px solid var(--clay-ring);
--flxn-badge-radius: 16px 20px 18px 22px;
}
/* ---------- 2. Light — soft studio ---------- */
[data-theme="clay"][data-theme][data-mantine-color-scheme="light"] {
--clay-surface: #f6f0e7;
--clay-surface-hover: #faf5ee;
--clay-well: #e5dccd;
--clay-border: #d3c8b8;
--clay-ring: #fffdf7;
--clay-inset: inset 0 3px 6px rgba(122, 104, 86, 0.28),
inset 0 -1px 0 rgba(255, 255, 255, 0.5);
--clay-inset-sm: inset 0 2px 4px rgba(122, 104, 86, 0.24),
inset 0 -1px 0 rgba(255, 255, 255, 0.45);
--mantine-color-body: #efe9e2;
--mantine-color-text: #3a3128;
--mantine-color-bright: #241f19;
--mantine-color-dimmed: #6d6156;
--mantine-color-placeholder: #837568;
--mantine-color-anchor: var(--mantine-primary-color-light-color);
--mantine-color-error: var(--mantine-color-red-7);
--mantine-color-default: #f6f0e7;
--mantine-color-default-hover: #faf5ee;
--mantine-color-default-color: #3a3128;
--mantine-color-default-border: #d3c8b8;
/* primaryShade 4 gives chalky plasticine fills, but Mantine then derives
chalky light-variant/text/outline colors that fail contrast on the light
backdrop. Re-point every ramp's aliases at deeper shades so
--mantine-primary-color-* keeps following the user's accent. */
--mantine-color-blue-light: color-mix(in srgb, var(--mantine-color-blue-4) 18%, transparent);
--mantine-color-blue-light-hover: color-mix(in srgb, var(--mantine-color-blue-4) 26%, transparent);
--mantine-color-blue-light-color: var(--mantine-color-blue-8);
--mantine-color-blue-text: var(--mantine-color-blue-8);
--mantine-color-blue-outline: var(--mantine-color-blue-7);
--mantine-color-red-light: color-mix(in srgb, var(--mantine-color-red-4) 18%, transparent);
--mantine-color-red-light-hover: color-mix(in srgb, var(--mantine-color-red-4) 26%, transparent);
--mantine-color-red-light-color: var(--mantine-color-red-8);
--mantine-color-red-text: var(--mantine-color-red-8);
--mantine-color-red-outline: var(--mantine-color-red-7);
--mantine-color-green-light: color-mix(in srgb, var(--mantine-color-green-4) 18%, transparent);
--mantine-color-green-light-hover: color-mix(in srgb, var(--mantine-color-green-4) 26%, transparent);
--mantine-color-green-light-color: var(--mantine-color-green-8);
--mantine-color-green-text: var(--mantine-color-green-8);
--mantine-color-green-outline: var(--mantine-color-green-7);
--mantine-color-yellow-light: color-mix(in srgb, var(--mantine-color-yellow-4) 18%, transparent);
--mantine-color-yellow-light-hover: color-mix(in srgb, var(--mantine-color-yellow-4) 26%, transparent);
--mantine-color-yellow-light-color: var(--mantine-color-yellow-8);
--mantine-color-yellow-text: var(--mantine-color-yellow-8);
--mantine-color-yellow-outline: var(--mantine-color-yellow-7);
--mantine-color-grape-light: color-mix(in srgb, var(--mantine-color-grape-4) 18%, transparent);
--mantine-color-grape-light-hover: color-mix(in srgb, var(--mantine-color-grape-4) 26%, transparent);
--mantine-color-grape-light-color: var(--mantine-color-grape-8);
--mantine-color-grape-text: var(--mantine-color-grape-8);
--mantine-color-grape-outline: var(--mantine-color-grape-7);
--mantine-color-orange-light: color-mix(in srgb, var(--mantine-color-orange-4) 18%, transparent);
--mantine-color-orange-light-hover: color-mix(in srgb, var(--mantine-color-orange-4) 26%, transparent);
--mantine-color-orange-light-color: var(--mantine-color-orange-8);
--mantine-color-orange-text: var(--mantine-color-orange-8);
--mantine-color-orange-outline: var(--mantine-color-orange-7);
--mantine-color-pink-light: color-mix(in srgb, var(--mantine-color-pink-4) 18%, transparent);
--mantine-color-pink-light-hover: color-mix(in srgb, var(--mantine-color-pink-4) 26%, transparent);
--mantine-color-pink-light-color: var(--mantine-color-pink-8);
--mantine-color-pink-text: var(--mantine-color-pink-8);
--mantine-color-pink-outline: var(--mantine-color-pink-7);
--mantine-color-lime-light: color-mix(in srgb, var(--mantine-color-lime-4) 18%, transparent);
--mantine-color-lime-light-hover: color-mix(in srgb, var(--mantine-color-lime-4) 26%, transparent);
--mantine-color-lime-light-color: var(--mantine-color-lime-8);
--mantine-color-lime-text: var(--mantine-color-lime-8);
--mantine-color-lime-outline: var(--mantine-color-lime-7);
/* pinched elevation: inner top highlight + soft warm drop */
--mantine-shadow-xs: inset 0 1px 0 rgba(255, 255, 255, 0.55),
0 3px 8px rgba(122, 104, 86, 0.18);
--mantine-shadow-sm: inset 0 2px 3px rgba(255, 255, 255, 0.6),
0 6px 14px rgba(122, 104, 86, 0.22);
--mantine-shadow-md: inset 0 2px 4px rgba(255, 255, 255, 0.6),
0 10px 22px rgba(122, 104, 86, 0.26);
--mantine-shadow-lg: inset 0 2px 4px rgba(255, 255, 255, 0.62),
0 14px 30px rgba(122, 104, 86, 0.3);
--mantine-shadow-xl: inset 0 3px 5px rgba(255, 255, 255, 0.65),
0 20px 40px rgba(122, 104, 86, 0.34);
/* app tokens */
--flxn-gold: #8a6716;
--flxn-silver: #6f665c;
--flxn-bronze: #8a5a34;
--flxn-toast-success: #538148;
--flxn-toast-error: #b0543a;
--flxn-podium-1-bg: color-mix(in srgb, #e0b545 12%, #f6f0e7);
--flxn-podium-1-border: color-mix(in srgb, #a67d24 40%, #d3c8b8);
--flxn-podium-1-label: #7c5d1b;
--flxn-podium-2-bg: color-mix(in srgb, #97887a 12%, #f6f0e7);
--flxn-podium-2-border: color-mix(in srgb, #786a5d 35%, #d3c8b8);
--flxn-podium-2-label: #5d5248;
--flxn-podium-3-bg: color-mix(in srgb, #c97a3d 12%, #f6f0e7);
--flxn-podium-3-border: color-mix(in srgb, #a86230 38%, #d3c8b8);
--flxn-podium-3-label: #7d4a22;
--flxn-showcase-bg: #f6f0e7;
--flxn-overlay: rgba(58, 49, 40, 0.45);
--flxn-dotgrid-color: #d9cfc1;
--flxn-pop-shadow: rgba(122, 104, 86, 0.4);
}
/* ---------- 2. Dark — night studio, warm key light ---------- */
[data-theme="clay"][data-theme][data-mantine-color-scheme="dark"] {
--clay-surface: #332c30;
--clay-surface-hover: #3b3336;
--clay-well: #1f1a1d;
--clay-border: #4a3f42;
--clay-ring: #4a4046;
--clay-inset: inset 0 3px 6px rgba(0, 0, 0, 0.5),
inset 0 -1px 0 rgba(255, 225, 185, 0.08);
--clay-inset-sm: inset 0 2px 4px rgba(0, 0, 0, 0.45),
inset 0 -1px 0 rgba(255, 225, 185, 0.07);
--mantine-color-body: #262023;
--mantine-color-text: #efe6dc;
--mantine-color-bright: #fbf6ef;
--mantine-color-dimmed: #b3a79d;
--mantine-color-placeholder: #8c7f76;
--mantine-color-anchor: var(--mantine-primary-color-3);
--mantine-color-error: var(--mantine-color-red-3);
--mantine-color-default: #332c30;
--mantine-color-default-hover: #3b3336;
--mantine-color-default-color: #efe6dc;
--mantine-color-default-border: #4a3f42;
/* warm inner highlight, deep studio shadows */
--mantine-shadow-xs: inset 0 1px 0 rgba(255, 225, 185, 0.1),
0 3px 8px rgba(0, 0, 0, 0.4);
--mantine-shadow-sm: inset 0 2px 3px rgba(255, 225, 185, 0.12),
0 8px 16px rgba(0, 0, 0, 0.45);
--mantine-shadow-md: inset 0 2px 4px rgba(255, 225, 185, 0.13),
0 12px 24px rgba(0, 0, 0, 0.5);
--mantine-shadow-lg: inset 0 2px 4px rgba(255, 225, 185, 0.14),
0 16px 32px rgba(0, 0, 0, 0.55);
--mantine-shadow-xl: inset 0 3px 5px rgba(255, 225, 185, 0.15),
0 22px 44px rgba(0, 0, 0, 0.6);
/* app tokens */
--flxn-gold: #e0bd6a;
--flxn-silver: #b8ada2;
--flxn-bronze: #d69a6e;
--flxn-toast-success: #9cc98e;
--flxn-toast-error: #eda28c;
--flxn-podium-1-bg: color-mix(in srgb, #e0bd6a 13%, #332c30);
--flxn-podium-1-border: color-mix(in srgb, #e0bd6a 32%, #4a3f42);
--flxn-podium-1-label: #e0bd6a;
--flxn-podium-2-bg: color-mix(in srgb, #b8ada2 11%, #332c30);
--flxn-podium-2-border: color-mix(in srgb, #b8ada2 28%, #4a3f42);
--flxn-podium-2-label: #b8ada2;
--flxn-podium-3-bg: color-mix(in srgb, #d69a6e 12%, #332c30);
--flxn-podium-3-border: color-mix(in srgb, #d69a6e 28%, #4a3f42);
--flxn-podium-3-label: #d69a6e;
--flxn-showcase-bg: #332c30;
--flxn-overlay: rgba(0, 0, 0, 0.55);
--flxn-dotgrid-color: rgba(255, 235, 215, 0.12);
--flxn-pop-shadow: rgba(0, 0, 0, 0.6);
}
/* ---------- 3. Type flavor ---------- */
[data-theme="clay"] body {
font-weight: 500;
}
/* ---------- 4. Backdrop — studio key light + clay grain ---------- */
[data-theme="clay"] body::before {
content: "";
position: fixed;
inset: 0;
z-index: -1; /* behind content — never tints text or surfaces */
pointer-events: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.05'/%3E%3C/svg%3E");
}
[data-theme="clay"][data-mantine-color-scheme="light"] body::before {
background-image: radial-gradient(
120% 70% at 50% 0%,
rgba(255, 252, 244, 0.65),
transparent 65%
),
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.05'/%3E%3C/svg%3E");
}
[data-theme="clay"][data-mantine-color-scheme="dark"] body::before {
background-image: radial-gradient(
110% 65% at 50% 0%,
rgba(255, 208, 158, 0.09),
transparent 62%
),
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.04'/%3E%3C/svg%3E");
}
/* ---------- 5. Clay blob surfaces ---------- */
[data-theme="clay"] .mantine-Paper-root {
background-color: var(--clay-surface);
border: 1px solid var(--clay-border);
border-radius: var(--clay-blob-md);
box-shadow: var(--mantine-shadow-sm);
}
[data-theme="clay"] .mantine-Card-root {
border-radius: var(--clay-blob-card);
}
[data-theme="clay"] .mantine-Popover-dropdown,
[data-theme="clay"] .mantine-Menu-dropdown,
[data-theme="clay"] .mantine-Modal-content,
[data-theme="clay"] .mantine-Notification-root {
background-color: var(--clay-surface);
border: 1px solid var(--clay-border);
box-shadow: var(--mantine-shadow-lg);
}
[data-theme="clay"] .mantine-Modal-content {
border-radius: var(--clay-blob-card);
}
[data-theme="clay"] .mantine-Modal-header {
background-color: var(--clay-surface);
}
[data-theme="clay"] .mantine-Notification-root {
border-radius: var(--clay-blob-md);
}
[data-theme="clay"] .mantine-Alert-root {
border-radius: var(--clay-blob-md);
box-shadow: var(--mantine-shadow-xs);
}
[data-theme="clay"] .mantine-Tooltip-tooltip {
border-radius: var(--clay-blob-pellet);
font-weight: 600;
box-shadow: var(--mantine-shadow-xs);
}
[data-theme="clay"][data-mantine-color-scheme="light"] .mantine-Tooltip-tooltip {
background-color: #3a3128;
color: #f3ece2;
}
[data-theme="clay"][data-mantine-color-scheme="dark"] .mantine-Tooltip-tooltip {
background-color: #efe6dc;
color: #33291f;
}
/* App shell: rolled clay slabs, soft edges */
[data-theme="clay"] .mantine-AppShell-header {
background-color: var(--clay-surface);
border-bottom: 1px solid var(--clay-border);
box-shadow: var(--mantine-shadow-xs);
}
[data-theme="clay"] .mantine-AppShell-navbar {
background-color: var(--clay-surface);
border-top: 1px solid var(--clay-border);
border-inline-end: 1px solid var(--clay-border);
box-shadow: var(--mantine-shadow-xs);
}
/* ---------- 6. Thumbed buttons ---------- */
[data-theme="clay"] .mantine-Button-root {
border: 1px solid var(--clay-border);
border-radius: var(--clay-blob-btn);
font-family: "Baloo 2", "Nunito", system-ui, sans-serif;
font-weight: 700;
box-shadow: var(--mantine-shadow-sm);
transition: box-shadow 160ms ease, background-color 120ms ease-out;
}
[data-theme="clay"] .mantine-ActionIcon-root {
border: 1px solid var(--clay-border);
border-radius: var(--clay-blob-round);
box-shadow: var(--mantine-shadow-xs);
}
[data-theme="clay"] .mantine-Button-root:where([data-variant="filled"], :not([data-variant])),
[data-theme="clay"] .mantine-ActionIcon-root:where([data-variant="filled"], :not([data-variant])) {
border-color: color-mix(in srgb, var(--mantine-primary-color-filled) 78%, var(--clay-ink));
}
[data-theme="clay"] .mantine-Button-root:where([data-variant="default"], [data-variant="outline"]),
[data-theme="clay"] .mantine-ActionIcon-root:where([data-variant="default"], [data-variant="outline"]) {
background-color: var(--clay-surface);
}
[data-theme="clay"] .mantine-Button-root:where([data-variant="default"], [data-variant="outline"]):hover,
[data-theme="clay"] .mantine-ActionIcon-root:where([data-variant="default"], [data-variant="outline"]):hover {
background-color: var(--clay-surface-hover);
}
/* Ghost variants stay flat — no floating clay plates */
[data-theme="clay"] .mantine-Button-root:where([data-variant="subtle"], [data-variant="transparent"], [data-variant="white"]),
[data-theme="clay"] .mantine-ActionIcon-root:where([data-variant="subtle"], [data-variant="transparent"], [data-variant="white"]) {
border-color: transparent;
box-shadow: none;
}
[data-theme="clay"] .mantine-Button-root:active:not([data-disabled], :disabled, [data-loading]),
[data-theme="clay"] .mantine-ActionIcon-root:active:not([data-disabled], :disabled, [data-loading]) {
box-shadow: var(--clay-inset-sm);
}
[data-theme="clay"] .mantine-Button-root:where([data-disabled], :disabled),
[data-theme="clay"] .mantine-ActionIcon-root:where([data-disabled], :disabled) {
box-shadow: none;
}
/* Every chalky fill pairs with the dark clay ink, never white */
[data-theme="clay"] .mantine-Button-root:not([data-variant]):not(:disabled):not([data-disabled]),
[data-theme="clay"] .mantine-Button-root[data-variant="filled"]:not(:disabled):not([data-disabled]),
[data-theme="clay"] .mantine-ActionIcon-root:not([data-variant]):not(:disabled):not([data-disabled]),
[data-theme="clay"] .mantine-ActionIcon-root[data-variant="filled"]:not(:disabled):not([data-disabled]),
[data-theme="clay"] .mantine-Badge-root:not([data-variant]),
[data-theme="clay"] .mantine-Badge-root[data-variant="filled"],
[data-theme="clay"] .mantine-ThemeIcon-root:not([data-variant]),
[data-theme="clay"] .mantine-ThemeIcon-root[data-variant="filled"] {
color: var(--clay-ink);
}
/* Extra squish: pressed clay squashes wide and short */
@media (prefers-reduced-motion: no-preference) {
[data-theme="clay"] .flxn-press {
transition: transform 160ms cubic-bezier(0.34, 1.3, 0.5, 1);
}
[data-theme="clay"]
.flxn-press:active:not(:disabled):not([data-disabled]):not([data-loading]) {
transform: scale(1.02, 0.92);
}
}
/* ---------- 7. Inputs — thumb-pressed wells ---------- */
[data-theme="clay"] .mantine-Input-input {
background-color: var(--clay-surface);
border: 1px solid var(--clay-border);
border-radius: var(--clay-blob-md);
box-shadow: var(--clay-inset-sm);
transition: border-color 160ms ease, box-shadow 160ms ease;
}
[data-theme="clay"] .mantine-Input-input:focus,
[data-theme="clay"] .mantine-Input-input:focus-within {
border-color: var(--mantine-primary-color-filled);
box-shadow: var(--clay-inset);
}
[data-theme="clay"] .mantine-Input-input[data-error] {
border-color: var(--mantine-color-error);
}
[data-theme="clay"] .mantine-PinInput-field {
border-radius: var(--clay-blob-pellet);
font-family: "Baloo 2", "Nunito", system-ui, sans-serif;
}
/* ---------- 8. Clay pellet badges ---------- */
[data-theme="clay"] .mantine-Badge-root {
border: 1px solid var(--clay-border);
border-radius: var(--clay-blob-pellet);
font-family: "Baloo 2", "Nunito", system-ui, sans-serif;
font-weight: 700;
text-transform: none;
letter-spacing: 0.01em;
box-shadow: var(--mantine-shadow-xs);
}
[data-theme="clay"] .mantine-Badge-root:where([data-variant="filled"], :not([data-variant])) {
border-color: color-mix(in srgb, var(--mantine-primary-color-filled) 78%, var(--clay-ink));
}
[data-theme="clay"] .mantine-Badge-root[data-variant="default"] {
background-color: var(--clay-surface);
color: var(--mantine-color-text);
}
[data-theme="clay"] .mantine-ThemeIcon-root {
border-radius: var(--clay-blob-round);
}
[data-theme="clay"] .mantine-ThemeIcon-root[data-variant="default"] {
background-color: var(--clay-surface);
border: 1px solid var(--clay-border);
box-shadow: var(--mantine-shadow-xs);
}
/* ---------- 9. Selection controls ---------- */
/* SegmentedControl: pressed trough, raised clay pellet indicator */
[data-theme="clay"] .mantine-SegmentedControl-root {
background-color: var(--clay-well);
border: 1px solid var(--clay-border);
border-radius: var(--clay-blob-md);
box-shadow: var(--clay-inset-sm);
}
[data-theme="clay"] .mantine-SegmentedControl-indicator {
background-color: var(--clay-surface);
border: 1px solid var(--clay-border);
border-radius: var(--clay-blob-pellet);
box-shadow: var(--mantine-shadow-xs);
}
[data-theme="clay"] .mantine-SegmentedControl-label {
font-weight: 700;
}
[data-theme="clay"] .mantine-SegmentedControl-label[data-active] {
color: var(--mantine-color-bright);
}
/* Switch: trough + rolled clay ball thumb */
[data-theme="clay"] .mantine-Switch-track {
border: 1px solid var(--clay-border);
border-radius: 999px;
box-shadow: var(--clay-inset-sm);
}
[data-theme="clay"] .mantine-Switch-thumb {
border: 1px solid var(--clay-border);
border-radius: var(--clay-blob-round);
box-shadow: var(--mantine-shadow-xs);
}
[data-theme="clay"] .mantine-Checkbox-input {
border: 2px solid var(--clay-border);
border-radius: 9px 11px 10px 12px / 11px 9px 12px 10px;
}
[data-theme="clay"] .mantine-Checkbox-input:not(:checked) {
background-color: var(--clay-surface);
box-shadow: var(--clay-inset-sm);
}
/* Progress: finger-groove trough */
[data-theme="clay"] .mantine-Progress-root {
background-color: var(--clay-well);
border: 1px solid var(--clay-border);
border-radius: 999px;
box-shadow: var(--clay-inset-sm);
}
[data-theme="clay"] .mantine-Progress-section {
border-radius: 999px;
}
/* ---------- 10. Navigation & misc ---------- */
[data-theme="clay"] .mantine-Tabs-list {
border-bottom-color: transparent;
gap: 0.25rem;
}
[data-theme="clay"] .mantine-Tabs-tab {
border: 0;
border-radius: var(--clay-blob-pellet);
font-weight: 700;
}
[data-theme="clay"] .mantine-Tabs-tab[data-active] {
background-color: var(--mantine-primary-color-light);
color: var(--mantine-primary-color-light-color);
}
[data-theme="clay"] .mantine-NavLink-root {
border: 1px solid transparent;
border-radius: var(--clay-blob-md);
font-weight: 600;
}
[data-theme="clay"] .mantine-NavLink-root[data-active] {
background-color: var(--mantine-primary-color-light);
border-color: var(--clay-border);
color: var(--mantine-primary-color-light-color);
}
[data-theme="clay"] .mantine-Skeleton-root {
border-radius: var(--clay-blob-md);
}
[data-theme="clay"] .mantine-Divider-root {
--divider-color: var(--clay-border);
}
[data-theme="clay"] .mantine-Loader-root {
--loader-color: var(--mantine-primary-color-filled);
}
/* ---------- 11. App component hooks ---------- */
[data-theme="clay"] .flxn-tabs[data-stuck] {
background-color: var(--mantine-color-body);
border-bottom-color: var(--clay-border);
box-shadow: 0 8px 16px -8px var(--flxn-pop-shadow);
}
[data-theme="clay"] .flxn-tab.flxn-tab {
border-radius: var(--clay-blob-pellet);
font-weight: 600;
}
[data-theme="clay"] .flxn-tab.flxn-tab[data-active] {
color: var(--flxn-on-accent);
font-weight: 700;
}
[data-theme="clay"] .flxn-tabs-indicator.flxn-tabs-indicator {
border-bottom: none;
border-radius: var(--clay-blob-pellet);
background-color: var(--mantine-primary-color-filled);
box-shadow: var(--clay-inset-sm);
}
/* Nav grid tiles: pinched clay pats, radii varied like hand-rolled pieces */
[data-theme="clay"] .flxn-tile.flxn-tile {
background-color: var(--clay-surface);
border: 1px solid var(--clay-border);
border-radius: var(--clay-blob-card);
box-shadow: var(--mantine-shadow-sm);
}
[data-theme="clay"] .flxn-tile.flxn-tile:nth-child(even) {
border-radius: var(--clay-blob-card-alt);
}
@media (hover: hover) {
[data-theme="clay"] .flxn-tile.flxn-tile:hover:not(:disabled) {
background-color: var(--clay-surface-hover);
}
}
[data-theme="clay"] .flxn-tile.flxn-tile:active:not(:disabled) {
box-shadow: var(--clay-inset-sm);
}
/* Flat glyph tint — no plates behind icons */
[data-theme="clay"] .flxn-tile-icon.flxn-tile-icon {
color: var(--mantine-primary-color-light-color);
}
/* "See all" as a small clay chip: accent wash + wash on-color */
[data-theme="clay"] .flxn-see-all.flxn-see-all {
font-family: "Baloo 2", "Nunito", system-ui, sans-serif;
font-weight: 700;
background-color: var(--mantine-primary-color-light);
border: 1px solid color-mix(in srgb, var(--mantine-primary-color-light-color) 30%, transparent);
border-radius: var(--clay-blob-pellet);
color: var(--mantine-primary-color-light-color);
}
@media (hover: hover) {
[data-theme="clay"] .flxn-see-all.flxn-see-all:hover {
background-color: var(--mantine-primary-color-light-hover);
text-decoration: none;
}
}
/* Match card + reaction tray: one pressed-together construction — the card
is a raised pat, the tray a thumb-groove in the same slab */
[data-theme="clay"] .flxn-match-card.flxn-match-card {
background-color: var(--clay-surface);
border: 1px solid var(--clay-border);
border-radius: var(--clay-blob-card);
box-shadow: var(--mantine-shadow-sm);
}
[data-theme="clay"] .flxn-reaction-row.flxn-reaction-row {
background-color: var(--clay-well);
border: 1px solid var(--clay-border);
border-radius: var(--clay-blob-md);
box-shadow: var(--clay-inset-sm);
}
/* Blobby avatar: the frame owns the single 3px clay ring */
[data-theme="clay"] .flxn-avatar-frame {
box-shadow: var(--mantine-shadow-xs);
}
+20
View File
@@ -0,0 +1,20 @@
import { msg } from "@lingui/core/macro";
import type { ThemeManifest } from "../types";
const manifest: ThemeManifest = {
id: "deco",
label: msg`Art Deco`,
colorSchemes: "both",
fontLinks: [
"https://fonts.googleapis.com/css2?family=Cinzel:wght@400..700&family=Josefin+Sans:ital,wght@0,300..700;1,300..700&display=swap",
],
chromeColors: {
light: { base: "#f6f1e5", dimmed: "#e7dfc9" },
dark: { base: "#16151a", dimmed: "#0e0d11" },
},
mantine: {
defaultRadius: 0,
},
};
export default manifest;
+619
View File
@@ -0,0 +1,619 @@
/* Art Deco — 1928 Gatsby luxury. Champagne gold on midnight ink (dark) and
ivory (light). Sharp geometry, double gold rules, gold-foil ornament.
Gold is STRUCTURAL decoration only; the interactive accent follows the
user's choice via the primary aliases over eight jewel-tone ramps. */
/* ------------------------------------------------------------------ */
/* 1. Tokens, any scheme */
/* ------------------------------------------------------------------ */
[data-theme="deco"][data-theme] {
--mantine-font-family: "Josefin Sans", "Avenir Next", "Century Gothic", sans-serif;
--mantine-font-family-headings: "Cinzel", "Trajan Pro", "Times New Roman", serif;
--mantine-heading-font-weight: 600;
--mantine-radius-default: 0;
--mantine-radius-xs: 0;
--mantine-radius-sm: 0;
--mantine-radius-md: 0;
--mantine-radius-lg: 0;
--mantine-radius-xl: 0;
--flxn-avatar-radius: 0;
--flxn-avatar-border: 1px solid var(--deco-rule);
--flxn-badge-radius: 0;
--mantine-color-white: #fcf9f0;
--mantine-color-black: #16151a;
/* Jewel-tone accent ramps. Shade 6 (light filled/text) holds >= 4.5:1
against ivory; shades 3-4 (dark text/anchor) read on midnight ink. */
/* blue -> sapphire */
--mantine-color-blue-0: #eaf1fb;
--mantine-color-blue-1: #cfdff4;
--mantine-color-blue-2: #a9c6e8;
--mantine-color-blue-3: #7fa8d8;
--mantine-color-blue-4: #5a8ac6;
--mantine-color-blue-5: #3d6eb0;
--mantine-color-blue-6: #2c5697;
--mantine-color-blue-7: #224477;
--mantine-color-blue-8: #19345c;
--mantine-color-blue-9: #112544;
/* red -> ruby */
--mantine-color-red-0: #fdecee;
--mantine-color-red-1: #f7cdd2;
--mantine-color-red-2: #eea6af;
--mantine-color-red-3: #e17d8a;
--mantine-color-red-4: #cf5766;
--mantine-color-red-5: #b93a4c;
--mantine-color-red-6: #9e2b3f;
--mantine-color-red-7: #822235;
--mantine-color-red-8: #651a2a;
--mantine-color-red-9: #4a121f;
/* green -> emerald */
--mantine-color-green-0: #e6f5ee;
--mantine-color-green-1: #c6e8d8;
--mantine-color-green-2: #9cd6bc;
--mantine-color-green-3: #6fc19e;
--mantine-color-green-4: #46aa82;
--mantine-color-green-5: #21936a;
--mantine-color-green-6: #147a57;
--mantine-color-green-7: #0f6247;
--mantine-color-green-8: #0b4c37;
--mantine-color-green-9: #073828;
/* yellow -> champagne gold */
--mantine-color-yellow-0: #fbf6e3;
--mantine-color-yellow-1: #f7ecc4;
--mantine-color-yellow-2: #f5e08c;
--mantine-color-yellow-3: #eacf6d;
--mantine-color-yellow-4: #dfbd52;
--mantine-color-yellow-5: #d4af37;
--mantine-color-yellow-6: #8a6508;
--mantine-color-yellow-7: #6f5106;
--mantine-color-yellow-8: #563e05;
--mantine-color-yellow-9: #3d2c03;
/* grape -> amethyst */
--mantine-color-grape-0: #f4edfa;
--mantine-color-grape-1: #e2d3f2;
--mantine-color-grape-2: #cdb1e8;
--mantine-color-grape-3: #b58ade;
--mantine-color-grape-4: #9c6ecb;
--mantine-color-grape-5: #7f4cb3;
--mantine-color-grape-6: #663693;
--mantine-color-grape-7: #532b79;
--mantine-color-grape-8: #40215e;
--mantine-color-grape-9: #2e1844;
/* orange -> amber */
--mantine-color-orange-0: #fdf1e3;
--mantine-color-orange-1: #f9dcbb;
--mantine-color-orange-2: #f2c184;
--mantine-color-orange-3: #e8a355;
--mantine-color-orange-4: #da8730;
--mantine-color-orange-5: #c66f15;
--mantine-color-orange-6: #a85a0c;
--mantine-color-orange-7: #8a4a09;
--mantine-color-orange-8: #6b3a07;
--mantine-color-orange-9: #4d2a05;
/* pink -> rose */
--mantine-color-pink-0: #fbedf3;
--mantine-color-pink-1: #f5cfe0;
--mantine-color-pink-2: #eba7c6;
--mantine-color-pink-3: #dd7aa8;
--mantine-color-pink-4: #c9538b;
--mantine-color-pink-5: #b23a72;
--mantine-color-pink-6: #a12a5e;
--mantine-color-pink-7: #862150;
--mantine-color-pink-8: #681940;
--mantine-color-pink-9: #4b122e;
/* lime -> peridot */
--mantine-color-lime-0: #f5f7e1;
--mantine-color-lime-1: #e8edbd;
--mantine-color-lime-2: #d5df90;
--mantine-color-lime-3: #c0cd64;
--mantine-color-lime-4: #a8b83f;
--mantine-color-lime-5: #8c9c26;
--mantine-color-lime-6: #5f6e12;
--mantine-color-lime-7: #4c580e;
--mantine-color-lime-8: #39430b;
--mantine-color-lime-9: #283007;
/* minimal shadows with a whisper of gold edge-glow */
--mantine-shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.16);
--mantine-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.18), 0 0 0 1px rgba(212, 175, 55, 0.08);
--mantine-shadow-md: 0 2px 6px rgba(0, 0, 0, 0.2), 0 0 10px rgba(212, 175, 55, 0.07);
--mantine-shadow-lg: 0 4px 12px rgba(0, 0, 0, 0.24), 0 0 16px rgba(212, 175, 55, 0.09);
--mantine-shadow-xl: 0 8px 24px rgba(0, 0, 0, 0.28), 0 0 22px rgba(212, 175, 55, 0.1);
}
/* ------------------------------------------------------------------ */
/* 2. Per-scheme tokens */
/* ------------------------------------------------------------------ */
[data-theme="deco"][data-theme][data-mantine-color-scheme="dark"] {
--mantine-color-body: #16151a;
--mantine-color-text: #eae3d1;
--mantine-color-bright: #f6f1e5;
--mantine-color-dimmed: #a79d85;
--mantine-color-placeholder: #837a66;
--mantine-color-error: #d06a6d;
--mantine-color-default: #201e25;
--mantine-color-default-hover: #272430;
--mantine-color-default-color: #eae3d1;
--mantine-color-default-border: #4a4230;
/* ink-charcoal surface ramp */
--mantine-color-dark-0: #d6d0c2;
--mantine-color-dark-1: #aaa294;
--mantine-color-dark-2: #8a8274;
--mantine-color-dark-3: #5c564a;
--mantine-color-dark-4: #444038;
--mantine-color-dark-5: #302d31;
--mantine-color-dark-6: #232127;
--mantine-color-dark-7: #1b1a1f;
--mantine-color-dark-8: #141317;
--mantine-color-dark-9: #101014;
/* deep jewel fills in dark too (Mantine defaults to shade 8) */
--mantine-color-blue-filled: var(--mantine-color-blue-6);
--mantine-color-blue-filled-hover: var(--mantine-color-blue-5);
--mantine-color-red-filled: var(--mantine-color-red-6);
--mantine-color-red-filled-hover: var(--mantine-color-red-5);
--mantine-color-green-filled: var(--mantine-color-green-6);
--mantine-color-green-filled-hover: var(--mantine-color-green-5);
--mantine-color-yellow-filled: var(--mantine-color-yellow-6);
--mantine-color-yellow-filled-hover: var(--mantine-color-yellow-7);
--mantine-color-grape-filled: var(--mantine-color-grape-6);
--mantine-color-grape-filled-hover: var(--mantine-color-grape-5);
--mantine-color-orange-filled: var(--mantine-color-orange-6);
--mantine-color-orange-filled-hover: var(--mantine-color-orange-5);
--mantine-color-pink-filled: var(--mantine-color-pink-6);
--mantine-color-pink-filled-hover: var(--mantine-color-pink-5);
--mantine-color-lime-filled: var(--mantine-color-lime-6);
--mantine-color-lime-filled-hover: var(--mantine-color-lime-7);
/* soft-variant tints from the jewel mid-shades */
--mantine-color-blue-light: rgba(90, 138, 198, 0.14);
--mantine-color-blue-light-hover: rgba(90, 138, 198, 0.2);
--mantine-color-blue-outline-hover: rgba(90, 138, 198, 0.08);
--mantine-color-red-light: rgba(207, 87, 102, 0.14);
--mantine-color-red-light-hover: rgba(207, 87, 102, 0.2);
--mantine-color-red-outline-hover: rgba(207, 87, 102, 0.08);
--mantine-color-green-light: rgba(70, 170, 130, 0.14);
--mantine-color-green-light-hover: rgba(70, 170, 130, 0.2);
--mantine-color-green-outline-hover: rgba(70, 170, 130, 0.08);
--mantine-color-yellow-light: rgba(212, 175, 55, 0.14);
--mantine-color-yellow-light-hover: rgba(212, 175, 55, 0.2);
--mantine-color-yellow-outline-hover: rgba(212, 175, 55, 0.08);
--mantine-color-grape-light: rgba(156, 110, 203, 0.14);
--mantine-color-grape-light-hover: rgba(156, 110, 203, 0.2);
--mantine-color-grape-outline-hover: rgba(156, 110, 203, 0.08);
--mantine-color-orange-light: rgba(218, 135, 48, 0.14);
--mantine-color-orange-light-hover: rgba(218, 135, 48, 0.2);
--mantine-color-orange-outline-hover: rgba(218, 135, 48, 0.08);
--mantine-color-pink-light: rgba(201, 83, 139, 0.14);
--mantine-color-pink-light-hover: rgba(201, 83, 139, 0.2);
--mantine-color-pink-outline-hover: rgba(201, 83, 139, 0.08);
--mantine-color-lime-light: rgba(168, 184, 63, 0.14);
--mantine-color-lime-light-hover: rgba(168, 184, 63, 0.2);
--mantine-color-lime-outline-hover: rgba(168, 184, 63, 0.08);
--mantine-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(212, 175, 55, 0.1);
--mantine-shadow-md: 0 2px 8px rgba(0, 0, 0, 0.45), 0 0 12px rgba(212, 175, 55, 0.08);
/* deco internals */
--deco-rule: rgba(212, 175, 55, 0.55);
--deco-rule-faint: rgba(212, 175, 55, 0.28);
--deco-chrome: #1b1a20;
--deco-gold-text: #eacf6d;
--deco-ray: rgba(212, 175, 55, 0.045);
--deco-tray: #121116;
/* app tokens */
--flxn-gold: #d4af37;
--flxn-silver: #c9ccd6;
--flxn-bronze: #c08447;
--flxn-toast-success: #46aa82;
--flxn-toast-error: #d06a6d;
--flxn-podium-1-bg: linear-gradient(150deg, rgba(245, 224, 140, 0.16), rgba(184, 134, 11, 0.1) 55%, rgba(122, 86, 7, 0.16));
--flxn-podium-1-border: #b8860b;
--flxn-podium-1-label: #eacf6d;
--flxn-podium-2-bg: linear-gradient(150deg, rgba(232, 234, 240, 0.13), rgba(148, 153, 166, 0.08) 55%, rgba(96, 101, 114, 0.12));
--flxn-podium-2-border: #9aa0ad;
--flxn-podium-2-label: #cfd4de;
--flxn-podium-3-bg: linear-gradient(150deg, rgba(222, 152, 82, 0.14), rgba(160, 106, 53, 0.09) 55%, rgba(110, 70, 32, 0.14));
--flxn-podium-3-border: #a06a35;
--flxn-podium-3-label: #d9a066;
--flxn-showcase-bg: #1b1a20;
--flxn-overlay: rgba(8, 7, 10, 0.62);
--flxn-dotgrid-color: rgba(212, 175, 55, 0.16);
--flxn-pop-shadow: rgba(0, 0, 0, 0.5);
}
[data-theme="deco"][data-theme][data-mantine-color-scheme="light"] {
--mantine-color-body: #f6f1e5;
--mantine-color-text: #26200f;
--mantine-color-bright: #131008;
--mantine-color-dimmed: #6d6450;
--mantine-color-placeholder: #948a72;
--mantine-color-error: #9e2b3f;
--mantine-color-default: #fbf7ec;
--mantine-color-default-hover: #f0e9d6;
--mantine-color-default-color: #26200f;
--mantine-color-default-border: #d3c69f;
/* warm champagne gray ramp */
--mantine-color-gray-0: #f3edda;
--mantine-color-gray-1: #eae2ca;
--mantine-color-gray-2: #ddd2b4;
--mantine-color-gray-3: #cfc29e;
--mantine-color-gray-4: #b5a988;
--mantine-color-gray-5: #988d70;
--mantine-color-gray-6: #7a7059;
--mantine-color-gray-7: #5c5443;
--mantine-color-gray-8: #3f3a2e;
--mantine-color-gray-9: #26221a;
/* soft-variant tints from the jewel shade-6 fills */
--mantine-color-blue-light: rgba(44, 86, 151, 0.1);
--mantine-color-blue-light-hover: rgba(44, 86, 151, 0.16);
--mantine-color-blue-outline-hover: rgba(44, 86, 151, 0.06);
--mantine-color-red-light: rgba(158, 43, 63, 0.1);
--mantine-color-red-light-hover: rgba(158, 43, 63, 0.16);
--mantine-color-red-outline-hover: rgba(158, 43, 63, 0.06);
--mantine-color-green-light: rgba(20, 122, 87, 0.1);
--mantine-color-green-light-hover: rgba(20, 122, 87, 0.16);
--mantine-color-green-outline-hover: rgba(20, 122, 87, 0.06);
--mantine-color-yellow-light: rgba(138, 101, 8, 0.1);
--mantine-color-yellow-light-hover: rgba(138, 101, 8, 0.16);
--mantine-color-yellow-outline-hover: rgba(138, 101, 8, 0.06);
--mantine-color-grape-light: rgba(102, 54, 147, 0.1);
--mantine-color-grape-light-hover: rgba(102, 54, 147, 0.16);
--mantine-color-grape-outline-hover: rgba(102, 54, 147, 0.06);
--mantine-color-orange-light: rgba(168, 90, 12, 0.1);
--mantine-color-orange-light-hover: rgba(168, 90, 12, 0.16);
--mantine-color-orange-outline-hover: rgba(168, 90, 12, 0.06);
--mantine-color-pink-light: rgba(161, 42, 94, 0.1);
--mantine-color-pink-light-hover: rgba(161, 42, 94, 0.16);
--mantine-color-pink-outline-hover: rgba(161, 42, 94, 0.06);
--mantine-color-lime-light: rgba(95, 110, 18, 0.1);
--mantine-color-lime-light-hover: rgba(95, 110, 18, 0.16);
--mantine-color-lime-outline-hover: rgba(95, 110, 18, 0.06);
--mantine-shadow-sm: 0 1px 2px rgba(60, 45, 10, 0.12), 0 0 0 1px rgba(150, 109, 9, 0.08);
--mantine-shadow-md: 0 2px 6px rgba(60, 45, 10, 0.14), 0 0 10px rgba(150, 109, 9, 0.07);
/* deco internals — darker golds for on-ivory contrast */
--deco-rule: rgba(150, 109, 9, 0.6);
--deco-rule-faint: rgba(150, 109, 9, 0.32);
--deco-chrome: #f2ecda;
--deco-gold-text: #7c5a06;
--deco-ray: rgba(150, 109, 9, 0.05);
--deco-tray: #ede4cc;
/* app tokens */
--flxn-gold: #b8860b;
--flxn-silver: #7b8494;
--flxn-bronze: #9c5f28;
--flxn-toast-success: #147a57;
--flxn-toast-error: #9e2b3f;
--flxn-podium-1-bg: linear-gradient(150deg, #fdf6dd, #f5e6b2 55%, #ecd68b);
--flxn-podium-1-border: #c49a1e;
--flxn-podium-1-label: #563e05;
--flxn-podium-2-bg: linear-gradient(150deg, #f6f5f2, #e6e6e8 55%, #d7d8dc);
--flxn-podium-2-border: #a9adb8;
--flxn-podium-2-label: #565d6b;
--flxn-podium-3-bg: linear-gradient(150deg, #f9ecdb, #f0d8b6 55%, #e5c294);
--flxn-podium-3-border: #b07a3e;
--flxn-podium-3-label: #6b3a07;
--flxn-showcase-bg: #fdfaf1;
--flxn-overlay: rgba(24, 20, 8, 0.42);
--flxn-dotgrid-color: rgba(138, 101, 8, 0.28);
--flxn-pop-shadow: rgba(60, 45, 10, 0.28);
}
/* ------------------------------------------------------------------ */
/* 3. Typography */
/* ------------------------------------------------------------------ */
[data-theme="deco"] :is(h1, h2, h3, h4, h5, h6),
[data-theme="deco"] .mantine-Title-root {
letter-spacing: 0.06em;
font-weight: 600;
}
/* gold-foil h1 (decoration): solid gold fallback, gradient foil where supported */
[data-theme="deco"] h1 {
color: var(--deco-gold-text);
}
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
[data-theme="deco"] h1 {
background-image: linear-gradient(170deg, #f5e08c 0%, #d4af37 45%, #b8860b 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
color: transparent;
}
[data-theme="deco"][data-mantine-color-scheme="light"] h1 {
background-image: linear-gradient(170deg, #c49a1e 0%, #966d09 55%, #6e4f05 100%);
}
}
@media (forced-colors: active) {
[data-theme="deco"] h1 {
background-image: none;
color: CanvasText;
-webkit-text-fill-color: currentColor;
}
}
[data-theme="deco"] .mantine-Anchor-root {
text-underline-offset: 3px;
text-decoration-color: var(--deco-rule-faint);
}
/* ------------------------------------------------------------------ */
/* 4. Sunburst fan — faint gold rays from top center */
/* ------------------------------------------------------------------ */
[data-theme="deco"] body::before {
content: "";
position: fixed;
inset: 0;
z-index: 9998;
pointer-events: none;
background: repeating-conic-gradient(
from 240deg at 50% -12%,
var(--deco-ray) 0deg 3deg,
transparent 3deg 8deg
);
-webkit-mask-image: linear-gradient(to bottom, #000, transparent 42%);
mask-image: linear-gradient(to bottom, #000, transparent 42%);
}
/* ------------------------------------------------------------------ */
/* 5. Surfaces — double gold rules */
/* ------------------------------------------------------------------ */
[data-theme="deco"] .mantine-Paper-root {
border-radius: 0;
}
[data-theme="deco"] .mantine-Card-root,
[data-theme="deco"] .mantine-Modal-content,
[data-theme="deco"] .mantine-Notification-root {
border: 1px solid var(--deco-rule);
outline: 1px solid var(--deco-rule-faint);
outline-offset: -5px;
box-shadow: var(--mantine-shadow-sm);
}
[data-theme="deco"] .mantine-Popover-dropdown,
[data-theme="deco"] .mantine-Alert-root {
border: 1px solid var(--deco-rule-faint);
}
[data-theme="deco"] .mantine-Modal-header {
border-bottom: 1px solid var(--deco-rule-faint);
}
[data-theme="deco"] .mantine-Tooltip-tooltip {
border: 1px solid var(--deco-rule-faint);
border-radius: 0;
}
/* ------------------------------------------------------------------ */
/* 6. App shell — gold double rules on header/navbar */
/* ------------------------------------------------------------------ */
[data-theme="deco"] .mantine-AppShell-header {
background-color: var(--deco-chrome);
border-bottom: 1px solid var(--deco-rule);
box-shadow: 0 4px 0 -3px var(--deco-rule-faint);
}
[data-theme="deco"] .mantine-AppShell-navbar {
background-color: var(--deco-chrome);
border-top: 1px solid var(--deco-rule);
box-shadow: 0 -4px 0 -3px var(--deco-rule-faint);
}
/* ------------------------------------------------------------------ */
/* 7. Buttons — jewel accent fill inside a thin gold rim */
/* ------------------------------------------------------------------ */
[data-theme="deco"] .mantine-Button-root {
text-transform: uppercase;
letter-spacing: 0.08em;
font-weight: 600;
}
[data-theme="deco"] .mantine-Button-root[data-variant="filled"],
[data-theme="deco"] .mantine-ActionIcon-root[data-variant="filled"] {
border: 1px solid var(--deco-rule);
}
[data-theme="deco"] .mantine-Button-root[data-variant="default"],
[data-theme="deco"] .mantine-Button-root[data-variant="outline"] {
border: 1px solid var(--deco-rule);
}
[data-theme="deco"] .mantine-Button-root:disabled,
[data-theme="deco"] .mantine-Button-root[data-disabled],
[data-theme="deco"] .mantine-ActionIcon-root:disabled,
[data-theme="deco"] .mantine-ActionIcon-root[data-disabled] {
background: var(--mantine-color-default);
color: var(--mantine-color-dimmed);
border: 1px solid var(--mantine-color-default-border);
}
/* ------------------------------------------------------------------ */
/* 8. Inputs */
/* ------------------------------------------------------------------ */
[data-theme="deco"] .mantine-Input-input,
[data-theme="deco"] .mantine-PinInput-input {
border: 1px solid var(--deco-rule-faint);
border-radius: 0;
}
[data-theme="deco"] .mantine-Input-input:focus,
[data-theme="deco"] .mantine-Input-input:focus-within,
[data-theme="deco"] .mantine-PinInput-input:focus {
border-color: var(--mantine-primary-color-filled);
box-shadow: 0 0 0 1px var(--deco-rule-faint);
}
/* ------------------------------------------------------------------ */
/* 9. Tabs / SegmentedControl / Badge / NavLink */
/* ------------------------------------------------------------------ */
[data-theme="deco"] .mantine-Tabs-tab {
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: var(--mantine-font-size-xs);
}
[data-theme="deco"] .mantine-Tabs-tab[data-active] {
color: var(--mantine-primary-color-light-color);
border-color: var(--flxn-gold);
}
[data-theme="deco"] .mantine-SegmentedControl-root {
border: 1px solid var(--deco-rule-faint);
background-color: var(--mantine-color-default);
}
[data-theme="deco"] .mantine-SegmentedControl-indicator {
background-color: var(--mantine-primary-color-filled);
border: 1px solid var(--deco-rule);
}
[data-theme="deco"] .mantine-SegmentedControl-label {
text-transform: uppercase;
letter-spacing: 0.06em;
}
[data-theme="deco"] .mantine-SegmentedControl-label[data-active] {
color: var(--mantine-color-white);
}
[data-theme="deco"] .mantine-Badge-root {
letter-spacing: 0.09em;
border: 1px solid color-mix(in srgb, currentColor 45%, transparent);
}
[data-theme="deco"] .mantine-NavLink-root[data-active] {
background-color: var(--mantine-primary-color-light);
color: var(--mantine-primary-color-light-color);
box-shadow: inset 2px 0 0 var(--flxn-gold);
}
/* ------------------------------------------------------------------ */
/* 10. App hooks — tab strip, tiles, see-all, match card */
/* ------------------------------------------------------------------ */
[data-theme="deco"] .flxn-tabs[data-stuck] {
background-color: var(--deco-chrome);
border-bottom-color: var(--deco-rule);
box-shadow: 0 4px 0 -3px var(--deco-rule-faint);
}
[data-theme="deco"] .flxn-tab.flxn-tab {
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: var(--mantine-font-size-xs);
}
[data-theme="deco"] .flxn-tab.flxn-tab[data-active] {
color: var(--mantine-primary-color-light-color);
font-weight: 600;
}
[data-theme="deco"] .flxn-tabs-indicator.flxn-tabs-indicator {
border-bottom: 2px solid var(--flxn-gold);
}
[data-theme="deco"] .flxn-tile {
background-color: var(--mantine-color-default);
border: 1px solid var(--deco-rule);
outline: 1px solid var(--deco-rule-faint);
outline-offset: -4px;
border-radius: 0;
box-shadow: var(--mantine-shadow-sm);
}
[data-theme="deco"] .flxn-tile-icon {
color: var(--mantine-primary-color-light-color);
}
[data-theme="deco"] .flxn-see-all.flxn-see-all {
border-radius: 0;
border: 1px solid var(--deco-rule);
background-color: transparent;
color: var(--mantine-primary-color-light-color);
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: var(--mantine-font-size-xs);
}
@media (hover: hover) {
[data-theme="deco"] .flxn-see-all.flxn-see-all:hover {
background-color: var(--mantine-primary-color-light);
}
}
[data-theme="deco"] .flxn-match-card {
border: 1px solid var(--deco-rule);
border-radius: 0;
box-shadow: var(--mantine-shadow-sm);
}
[data-theme="deco"] .flxn-reaction-row.flxn-reaction-row {
border: 1px solid var(--deco-rule);
border-radius: 0;
background-color: var(--deco-tray);
}
/* ------------------------------------------------------------------ */
/* 11. Divider — centered diamond ornament */
/* ------------------------------------------------------------------ */
[data-theme="deco"] .mantine-Divider-root {
--divider-color: var(--deco-rule-faint);
}
[data-theme="deco"] .mantine-Divider-root[data-orientation="horizontal"]:not(:has(.mantine-Divider-label)) {
position: relative;
overflow: visible;
}
[data-theme="deco"] .mantine-Divider-root[data-orientation="horizontal"]:not(:has(.mantine-Divider-label))::after {
content: "\25C6";
position: absolute;
left: 50%;
top: 0;
transform: translate(-50%, -50%);
font-size: 0.5rem;
line-height: 1;
color: var(--flxn-gold);
background-color: var(--mantine-color-body);
padding: 0 0.5rem;
pointer-events: none;
}
[data-theme="deco"] .mantine-Divider-label {
color: var(--deco-gold-text);
text-transform: uppercase;
letter-spacing: 0.14em;
font-size: 0.6875rem;
}
/* ------------------------------------------------------------------ */
/* 12. Press feedback re-flavor */
/* ------------------------------------------------------------------ */
@media (prefers-reduced-motion: no-preference) {
[data-theme="deco"] .flxn-press:active {
transform: scale(0.975);
}
}
+11
View File
@@ -0,0 +1,11 @@
import { msg } from "@lingui/core/macro";
import type { ThemeManifest } from "../types";
const manifest: ThemeManifest = {
id: "default",
label: msg`Default`,
colorSchemes: "both",
order: 0,
};
export default manifest;
+24
View File
@@ -0,0 +1,24 @@
/* Preview-contract values for the default theme so its picker card renders
correctly while another theme is active. Values mirror Mantine defaults +
the base theme object; the live app gets these from Mantine itself. */
[data-theme="default"][data-theme] {
--mantine-font-family: "Inter", sans-serif;
--mantine-font-family-headings: "League Spartan", sans-serif;
--mantine-radius-default: 0.25rem;
--mantine-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.05), rgba(0, 0, 0, 0.05) 0 10px 15px -5px, rgba(0, 0, 0, 0.04) 0 7px 7px -5px;
}
[data-theme="default"][data-theme][data-mantine-color-scheme="light"] {
--mantine-color-body: #ffffff;
--mantine-color-text: #000000;
--mantine-color-dimmed: #868e96;
--mantine-color-default-border: #ced4da;
}
[data-theme="default"][data-theme][data-mantine-color-scheme="dark"] {
--mantine-color-body: #242424;
--mantine-color-text: #c9c9c9;
--mantine-color-dimmed: #828282;
--mantine-color-default-border: #424242;
}
+18
View File
@@ -0,0 +1,18 @@
import { msg } from "@lingui/core/macro";
import type { ThemeManifest } from "../types";
const manifest: ThemeManifest = {
id: "gothic",
label: msg`Dark Academia`,
colorSchemes: "both",
fontLinks: [
"https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,500;0,600;0,700;1,500;1,600&display=swap",
"https://fonts.googleapis.com/css2?family=EB+Garamond:ital,wght@0,400;0,500;0,600;1,400;1,500&display=swap",
],
chromeColors: {
light: { base: "#efe6d5", dimmed: "#e2d4bc" },
dark: { base: "#0e0b10", dimmed: "#070509" },
},
};
export default manifest;
+641
View File
@@ -0,0 +1,641 @@
/* Dark Academia — candlelit library gothic.
Antique brass #b08d57 is the structural decoration (double borders, rules,
fleurons); the interactive accent follows the user via the primary aliases.
All eight accent ramps are retuned as heraldic manuscript pigments. */
/* ---------------------------------------------------------------- *
* 1. Scheme-independent tokens — (0,2,0)
* ---------------------------------------------------------------- */
[data-theme="gothic"][data-theme] {
/* Type: Garamond has a small x-height — sizes are bumped to compensate. */
--mantine-font-family: "EB Garamond", "Iowan Old Style", Georgia, serif;
--mantine-font-family-headings: "Cormorant Garamond", "EB Garamond", Georgia, serif;
--mantine-font-size-xs: 0.8125rem;
--mantine-font-size-sm: 0.9375rem;
--mantine-font-size-md: 1.0625rem;
--mantine-font-size-lg: 1.1875rem;
--mantine-font-size-xl: 1.375rem;
--mantine-heading-font-weight: 600;
--mantine-h1-font-size: 2.6rem;
--mantine-h1-line-height: 1.15;
--mantine-h2-font-size: 2.1rem;
--mantine-h2-line-height: 1.2;
--mantine-h3-font-size: 1.65rem;
--mantine-h3-line-height: 1.25;
--mantine-h4-font-size: 1.35rem;
--mantine-h4-line-height: 1.3;
--mantine-h5-font-size: 1.175rem;
--mantine-h5-line-height: 1.35;
--mantine-h6-font-size: 1.05rem;
--mantine-h6-line-height: 1.4;
/* Shape: near-square, engraved rather than rounded. */
--mantine-radius-xs: 0;
--mantine-radius-sm: 0.0625rem;
--mantine-radius-md: 0.125rem;
--mantine-radius-lg: 0.125rem;
--mantine-radius-xl: 0.125rem;
--mantine-radius-default: 0.125rem;
/* Elevation: deep, soft, black — never bright. */
--mantine-shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.45);
--mantine-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.5), 0 6px 16px rgba(0, 0, 0, 0.35);
--mantine-shadow-md: 0 2px 4px rgba(0, 0, 0, 0.5), 0 12px 28px rgba(0, 0, 0, 0.4);
--mantine-shadow-lg: 0 4px 8px rgba(0, 0, 0, 0.5), 0 20px 44px rgba(0, 0, 0, 0.45);
--mantine-shadow-xl: 0 6px 12px rgba(0, 0, 0, 0.55), 0 28px 64px rgba(0, 0, 0, 0.5);
/* Accent ramps retuned as heraldic manuscript pigments — muted and
ink-saturated; every shade 6 carries parchment contrast text. */
/* blue → lapis lazuli */
--mantine-color-blue-0: #edeff7;
--mantine-color-blue-1: #d7dcee;
--mantine-color-blue-2: #b6bede;
--mantine-color-blue-3: #8e9ac9;
--mantine-color-blue-4: #6879b1;
--mantine-color-blue-5: #4a5b97;
--mantine-color-blue-6: #374a80;
--mantine-color-blue-7: #2c3b69;
--mantine-color-blue-8: #222d52;
--mantine-color-blue-9: #18213c;
/* red → oxblood */
--mantine-color-red-0: #f9ecea;
--mantine-color-red-1: #f0d5d3;
--mantine-color-red-2: #e2b0ad;
--mantine-color-red-3: #d18a86;
--mantine-color-red-4: #bd5f5c;
--mantine-color-red-5: #a53a3f;
--mantine-color-red-6: #8a1c2b;
--mantine-color-red-7: #741522;
--mantine-color-red-8: #5e101b;
--mantine-color-red-9: #470b14;
/* green → forest */
--mantine-color-green-0: #ecf1ea;
--mantine-color-green-1: #d4e1d1;
--mantine-color-green-2: #b2c9af;
--mantine-color-green-3: #8aac88;
--mantine-color-green-4: #628e62;
--mantine-color-green-5: #427245;
--mantine-color-green-6: #2f5b35;
--mantine-color-green-7: #25492b;
--mantine-color-green-8: #1c3821;
--mantine-color-green-9: #132818;
/* yellow → gilded gold leaf */
--mantine-color-yellow-0: #faf3df;
--mantine-color-yellow-1: #f1e3ba;
--mantine-color-yellow-2: #e5cd8c;
--mantine-color-yellow-3: #d5b05c;
--mantine-color-yellow-4: #c1963a;
--mantine-color-yellow-5: #a87a1a;
--mantine-color-yellow-6: #8a600f;
--mantine-color-yellow-7: #6f4d0c;
--mantine-color-yellow-8: #573c0a;
--mantine-color-yellow-9: #402c07;
/* grape → royal purple */
--mantine-color-grape-0: #f2edf5;
--mantine-color-grape-1: #e1d6e9;
--mantine-color-grape-2: #c8b4d6;
--mantine-color-grape-3: #a98bbd;
--mantine-color-grape-4: #8965a3;
--mantine-color-grape-5: #6e4789;
--mantine-color-grape-6: #593272;
--mantine-color-grape-7: #48285d;
--mantine-color-grape-8: #381e49;
--mantine-color-grape-9: #291636;
/* orange → burnt amber */
--mantine-color-orange-0: #f9efe3;
--mantine-color-orange-1: #f1dcc0;
--mantine-color-orange-2: #e6c192;
--mantine-color-orange-3: #dc9d5c;
--mantine-color-orange-4: #cd7c33;
--mantine-color-orange-5: #b95f18;
--mantine-color-orange-6: #9c4a10;
--mantine-color-orange-7: #7e3c0d;
--mantine-color-orange-8: #62300b;
--mantine-color-orange-9: #482407;
/* pink → rose madder */
--mantine-color-pink-0: #f8edf0;
--mantine-color-pink-1: #efd6dc;
--mantine-color-pink-2: #e2b3c0;
--mantine-color-pink-3: #d18b9e;
--mantine-color-pink-4: #bc637e;
--mantine-color-pink-5: #a34463;
--mantine-color-pink-6: #882f4e;
--mantine-color-pink-7: #712540;
--mantine-color-pink-8: #591c32;
--mantine-color-pink-9: #421425;
/* lime → sage */
--mantine-color-lime-0: #f0f2ea;
--mantine-color-lime-1: #dfe3d0;
--mantine-color-lime-2: #c8cdae;
--mantine-color-lime-3: #aab187;
--mantine-color-lime-4: #8c9465;
--mantine-color-lime-5: #707a48;
--mantine-color-lime-6: #5a6337;
--mantine-color-lime-7: #48502c;
--mantine-color-lime-8: #373d22;
--mantine-color-lime-9: #292d19;
/* Gray ramp as parchment/ink so grey-derived UI stays in the world. */
--mantine-color-gray-0: #f6efe0;
--mantine-color-gray-1: #efe6d5;
--mantine-color-gray-2: #e2d5bc;
--mantine-color-gray-3: #cfc0a4;
--mantine-color-gray-4: #b3a487;
--mantine-color-gray-5: #8f8069;
--mantine-color-gray-6: #6b5d4b;
--mantine-color-gray-7: #4a3f33;
--mantine-color-gray-8: #352c23;
--mantine-color-gray-9: #2b2118;
/* Dark ramp as ink-black with a violet-umber cast. */
--mantine-color-dark-0: #e8e0d0;
--mantine-color-dark-1: #a89a8a;
--mantine-color-dark-2: #6e5f68;
--mantine-color-dark-3: #4a3e4a;
--mantine-color-dark-4: #322833;
--mantine-color-dark-5: #241c26;
--mantine-color-dark-6: #1a141c;
--mantine-color-dark-7: #141016;
--mantine-color-dark-8: #0e0b10;
--mantine-color-dark-9: #0a0709;
--flxn-pop-shadow: rgba(0, 0, 0, 0.55);
--flxn-avatar-radius: 50%; /* portrait miniatures */
--flxn-avatar-border: 1px solid var(--gothic-brass-faint);
--flxn-badge-radius: 2px;
}
/* ---------------------------------------------------------------- *
* 2. Per-scheme tokens — (0,3,0)
* ---------------------------------------------------------------- */
[data-theme="gothic"][data-theme][data-mantine-color-scheme="dark"] {
--mantine-color-body: #0e0b10;
--mantine-color-text: #e8e0d0;
--mantine-color-bright: #f6efe0;
--mantine-color-dimmed: #a89a82;
--mantine-color-placeholder: #7d7264;
--mantine-color-anchor: var(--mantine-primary-color-2);
--mantine-color-error: #d4736a;
--mantine-color-default: #171219;
--mantine-color-default-hover: #1f1822;
--mantine-color-default-color: #e8e0d0;
--mantine-color-default-border: #453729;
/* Accent variant vars re-derived from the retuned ramps (Mantine's
defaults are literal rgba of its stock hues). Filled stays mid-ramp:
shade 8 pigment vanishes on ink black. */
--mantine-color-blue-filled: var(--mantine-color-blue-6);
--mantine-color-blue-filled-hover: var(--mantine-color-blue-5);
--mantine-color-blue-light: color-mix(in srgb, var(--mantine-color-blue-4) 15%, transparent);
--mantine-color-blue-light-hover: color-mix(in srgb, var(--mantine-color-blue-4) 22%, transparent);
--mantine-color-blue-light-color: var(--mantine-color-blue-2);
--mantine-color-blue-outline: var(--mantine-color-blue-3);
--mantine-color-blue-outline-hover: color-mix(in srgb, var(--mantine-color-blue-3) 12%, transparent);
--mantine-color-blue-text: var(--mantine-color-blue-3);
--mantine-color-blue-contrast: #f3ead8;
--mantine-color-red-filled: var(--mantine-color-red-6);
--mantine-color-red-filled-hover: var(--mantine-color-red-5);
--mantine-color-red-light: color-mix(in srgb, var(--mantine-color-red-4) 15%, transparent);
--mantine-color-red-light-hover: color-mix(in srgb, var(--mantine-color-red-4) 22%, transparent);
--mantine-color-red-light-color: var(--mantine-color-red-2);
--mantine-color-red-outline: var(--mantine-color-red-3);
--mantine-color-red-outline-hover: color-mix(in srgb, var(--mantine-color-red-3) 12%, transparent);
--mantine-color-red-text: var(--mantine-color-red-3);
--mantine-color-red-contrast: #f3ead8;
--mantine-color-green-filled: var(--mantine-color-green-6);
--mantine-color-green-filled-hover: var(--mantine-color-green-5);
--mantine-color-green-light: color-mix(in srgb, var(--mantine-color-green-4) 15%, transparent);
--mantine-color-green-light-hover: color-mix(in srgb, var(--mantine-color-green-4) 22%, transparent);
--mantine-color-green-light-color: var(--mantine-color-green-2);
--mantine-color-green-outline: var(--mantine-color-green-3);
--mantine-color-green-outline-hover: color-mix(in srgb, var(--mantine-color-green-3) 12%, transparent);
--mantine-color-green-text: var(--mantine-color-green-3);
--mantine-color-green-contrast: #f3ead8;
--mantine-color-yellow-filled: var(--mantine-color-yellow-6);
--mantine-color-yellow-filled-hover: var(--mantine-color-yellow-5);
--mantine-color-yellow-light: color-mix(in srgb, var(--mantine-color-yellow-4) 15%, transparent);
--mantine-color-yellow-light-hover: color-mix(in srgb, var(--mantine-color-yellow-4) 22%, transparent);
--mantine-color-yellow-light-color: var(--mantine-color-yellow-2);
--mantine-color-yellow-outline: var(--mantine-color-yellow-3);
--mantine-color-yellow-outline-hover: color-mix(in srgb, var(--mantine-color-yellow-3) 12%, transparent);
--mantine-color-yellow-text: var(--mantine-color-yellow-3);
--mantine-color-yellow-contrast: #f3ead8;
--mantine-color-grape-filled: var(--mantine-color-grape-6);
--mantine-color-grape-filled-hover: var(--mantine-color-grape-5);
--mantine-color-grape-light: color-mix(in srgb, var(--mantine-color-grape-4) 15%, transparent);
--mantine-color-grape-light-hover: color-mix(in srgb, var(--mantine-color-grape-4) 22%, transparent);
--mantine-color-grape-light-color: var(--mantine-color-grape-2);
--mantine-color-grape-outline: var(--mantine-color-grape-3);
--mantine-color-grape-outline-hover: color-mix(in srgb, var(--mantine-color-grape-3) 12%, transparent);
--mantine-color-grape-text: var(--mantine-color-grape-3);
--mantine-color-grape-contrast: #f3ead8;
--mantine-color-orange-filled: var(--mantine-color-orange-6);
--mantine-color-orange-filled-hover: var(--mantine-color-orange-5);
--mantine-color-orange-light: color-mix(in srgb, var(--mantine-color-orange-4) 15%, transparent);
--mantine-color-orange-light-hover: color-mix(in srgb, var(--mantine-color-orange-4) 22%, transparent);
--mantine-color-orange-light-color: var(--mantine-color-orange-2);
--mantine-color-orange-outline: var(--mantine-color-orange-3);
--mantine-color-orange-outline-hover: color-mix(in srgb, var(--mantine-color-orange-3) 12%, transparent);
--mantine-color-orange-text: var(--mantine-color-orange-3);
--mantine-color-orange-contrast: #f3ead8;
--mantine-color-pink-filled: var(--mantine-color-pink-6);
--mantine-color-pink-filled-hover: var(--mantine-color-pink-5);
--mantine-color-pink-light: color-mix(in srgb, var(--mantine-color-pink-4) 15%, transparent);
--mantine-color-pink-light-hover: color-mix(in srgb, var(--mantine-color-pink-4) 22%, transparent);
--mantine-color-pink-light-color: var(--mantine-color-pink-2);
--mantine-color-pink-outline: var(--mantine-color-pink-3);
--mantine-color-pink-outline-hover: color-mix(in srgb, var(--mantine-color-pink-3) 12%, transparent);
--mantine-color-pink-text: var(--mantine-color-pink-3);
--mantine-color-pink-contrast: #f3ead8;
--mantine-color-lime-filled: var(--mantine-color-lime-6);
--mantine-color-lime-filled-hover: var(--mantine-color-lime-5);
--mantine-color-lime-light: color-mix(in srgb, var(--mantine-color-lime-4) 15%, transparent);
--mantine-color-lime-light-hover: color-mix(in srgb, var(--mantine-color-lime-4) 22%, transparent);
--mantine-color-lime-light-color: var(--mantine-color-lime-2);
--mantine-color-lime-outline: var(--mantine-color-lime-3);
--mantine-color-lime-outline-hover: color-mix(in srgb, var(--mantine-color-lime-3) 12%, transparent);
--mantine-color-lime-text: var(--mantine-color-lime-3);
--mantine-color-lime-contrast: #f3ead8;
--mantine-color-gray-light: color-mix(in srgb, var(--mantine-color-gray-4) 12%, transparent);
--mantine-color-gray-light-hover: color-mix(in srgb, var(--mantine-color-gray-4) 18%, transparent);
--mantine-color-gray-light-color: var(--mantine-color-gray-2);
/* Theme-local palette used by component rules below. */
--gothic-brass: #b08d57;
--gothic-brass-bright: #c9a86b;
--gothic-brass-faint: rgba(176, 141, 87, 0.38);
--gothic-brass-ghost: rgba(176, 141, 87, 0.14);
--gothic-surface: #17121a;
--gothic-recessed: #0a0709;
--gothic-shell: #120e14;
--gothic-line: #060409;
--gothic-line-highlight: rgba(232, 224, 208, 0.08);
/* App tokens: gold -> antique brass; manuscript podium tiers. */
--flxn-gold: #c9a86b;
--flxn-silver: #a8a196;
--flxn-bronze: #b57945;
--flxn-toast-success: #a3b98a;
--flxn-toast-error: #dc9a90;
--flxn-podium-1-bg: color-mix(in srgb, #b08d57 14%, #141016);
--flxn-podium-1-border: color-mix(in srgb, #b08d57 55%, #322833);
--flxn-podium-1-label: #d6b878;
--flxn-podium-2-bg: color-mix(in srgb, #a8a196 8%, #141016);
--flxn-podium-2-border: color-mix(in srgb, #a8a196 32%, #322833);
--flxn-podium-2-label: #b5aea1;
--flxn-podium-3-bg: color-mix(in srgb, #b57945 12%, #141016);
--flxn-podium-3-border: color-mix(in srgb, #b57945 45%, #322833);
--flxn-podium-3-label: #c08757;
--flxn-showcase-bg: #141016;
--flxn-overlay: rgba(6, 3, 8, 0.62);
--flxn-dotgrid-color: rgba(176, 141, 87, 0.18);
}
[data-theme="gothic"][data-theme][data-mantine-color-scheme="light"] {
--mantine-color-body: #efe6d5;
--mantine-color-text: #2b2118;
--mantine-color-bright: #1a140e;
--mantine-color-dimmed: #6b5d4b;
--mantine-color-placeholder: #857661;
--mantine-color-anchor: var(--mantine-primary-color-7);
--mantine-color-error: #9e2b20;
--mantine-color-default: #f4ecdc;
--mantine-color-default-hover: #ece1cb;
--mantine-color-default-color: #2b2118;
--mantine-color-default-border: #b9a67f;
/* Accent variant vars re-derived from the retuned ramps; ink-saturated
text shades on aged parchment. */
--mantine-color-blue-light: color-mix(in srgb, var(--mantine-color-blue-6) 10%, transparent);
--mantine-color-blue-light-hover: color-mix(in srgb, var(--mantine-color-blue-6) 16%, transparent);
--mantine-color-blue-light-color: var(--mantine-color-blue-7);
--mantine-color-blue-outline: var(--mantine-color-blue-6);
--mantine-color-blue-outline-hover: color-mix(in srgb, var(--mantine-color-blue-6) 8%, transparent);
--mantine-color-blue-text: var(--mantine-color-blue-7);
--mantine-color-blue-contrast: #f3ead8;
--mantine-color-red-light: color-mix(in srgb, var(--mantine-color-red-6) 10%, transparent);
--mantine-color-red-light-hover: color-mix(in srgb, var(--mantine-color-red-6) 16%, transparent);
--mantine-color-red-light-color: var(--mantine-color-red-7);
--mantine-color-red-outline: var(--mantine-color-red-6);
--mantine-color-red-outline-hover: color-mix(in srgb, var(--mantine-color-red-6) 8%, transparent);
--mantine-color-red-text: var(--mantine-color-red-7);
--mantine-color-red-contrast: #f3ead8;
--mantine-color-green-light: color-mix(in srgb, var(--mantine-color-green-6) 10%, transparent);
--mantine-color-green-light-hover: color-mix(in srgb, var(--mantine-color-green-6) 16%, transparent);
--mantine-color-green-light-color: var(--mantine-color-green-7);
--mantine-color-green-outline: var(--mantine-color-green-6);
--mantine-color-green-outline-hover: color-mix(in srgb, var(--mantine-color-green-6) 8%, transparent);
--mantine-color-green-text: var(--mantine-color-green-7);
--mantine-color-green-contrast: #f3ead8;
--mantine-color-yellow-light: color-mix(in srgb, var(--mantine-color-yellow-6) 10%, transparent);
--mantine-color-yellow-light-hover: color-mix(in srgb, var(--mantine-color-yellow-6) 16%, transparent);
--mantine-color-yellow-light-color: var(--mantine-color-yellow-7);
--mantine-color-yellow-outline: var(--mantine-color-yellow-6);
--mantine-color-yellow-outline-hover: color-mix(in srgb, var(--mantine-color-yellow-6) 8%, transparent);
--mantine-color-yellow-text: var(--mantine-color-yellow-7);
--mantine-color-yellow-contrast: #f3ead8;
--mantine-color-grape-light: color-mix(in srgb, var(--mantine-color-grape-6) 10%, transparent);
--mantine-color-grape-light-hover: color-mix(in srgb, var(--mantine-color-grape-6) 16%, transparent);
--mantine-color-grape-light-color: var(--mantine-color-grape-7);
--mantine-color-grape-outline: var(--mantine-color-grape-6);
--mantine-color-grape-outline-hover: color-mix(in srgb, var(--mantine-color-grape-6) 8%, transparent);
--mantine-color-grape-text: var(--mantine-color-grape-7);
--mantine-color-grape-contrast: #f3ead8;
--mantine-color-orange-light: color-mix(in srgb, var(--mantine-color-orange-6) 10%, transparent);
--mantine-color-orange-light-hover: color-mix(in srgb, var(--mantine-color-orange-6) 16%, transparent);
--mantine-color-orange-light-color: var(--mantine-color-orange-7);
--mantine-color-orange-outline: var(--mantine-color-orange-6);
--mantine-color-orange-outline-hover: color-mix(in srgb, var(--mantine-color-orange-6) 8%, transparent);
--mantine-color-orange-text: var(--mantine-color-orange-7);
--mantine-color-orange-contrast: #f3ead8;
--mantine-color-pink-light: color-mix(in srgb, var(--mantine-color-pink-6) 10%, transparent);
--mantine-color-pink-light-hover: color-mix(in srgb, var(--mantine-color-pink-6) 16%, transparent);
--mantine-color-pink-light-color: var(--mantine-color-pink-7);
--mantine-color-pink-outline: var(--mantine-color-pink-6);
--mantine-color-pink-outline-hover: color-mix(in srgb, var(--mantine-color-pink-6) 8%, transparent);
--mantine-color-pink-text: var(--mantine-color-pink-7);
--mantine-color-pink-contrast: #f3ead8;
--mantine-color-lime-light: color-mix(in srgb, var(--mantine-color-lime-6) 10%, transparent);
--mantine-color-lime-light-hover: color-mix(in srgb, var(--mantine-color-lime-6) 16%, transparent);
--mantine-color-lime-light-color: var(--mantine-color-lime-7);
--mantine-color-lime-outline: var(--mantine-color-lime-6);
--mantine-color-lime-outline-hover: color-mix(in srgb, var(--mantine-color-lime-6) 8%, transparent);
--mantine-color-lime-text: var(--mantine-color-lime-7);
--mantine-color-lime-contrast: #f3ead8;
--mantine-color-gray-light: color-mix(in srgb, var(--mantine-color-gray-6) 10%, transparent);
--mantine-color-gray-light-hover: color-mix(in srgb, var(--mantine-color-gray-6) 15%, transparent);
--mantine-color-gray-light-color: var(--mantine-color-gray-7);
--gothic-brass: #8a6a34;
--gothic-brass-bright: #7a5a28;
--gothic-brass-faint: rgba(138, 106, 52, 0.45);
--gothic-brass-ghost: rgba(138, 106, 52, 0.12);
--gothic-surface: #f6efe0;
--gothic-recessed: #e4d6bd;
--gothic-shell: #e9dec9;
--gothic-line: rgba(74, 58, 38, 0.45);
--gothic-line-highlight: rgba(255, 252, 240, 0.85);
--flxn-gold: #7a5a16;
--flxn-silver: #7b7264;
--flxn-bronze: #8c4f22;
--flxn-toast-success: #4d6b34;
--flxn-toast-error: #9e2b20;
--flxn-podium-1-bg: color-mix(in srgb, #b08d57 16%, #f6efe0);
--flxn-podium-1-border: color-mix(in srgb, #8a6a34 55%, #cfc0a4);
--flxn-podium-1-label: #6f5116;
--flxn-podium-2-bg: color-mix(in srgb, #8f8069 10%, #f6efe0);
--flxn-podium-2-border: #b3a487;
--flxn-podium-2-label: #6b5d4b;
--flxn-podium-3-bg: color-mix(in srgb, #8c4f22 12%, #f6efe0);
--flxn-podium-3-border: color-mix(in srgb, #8c4f22 45%, #cfc0a4);
--flxn-podium-3-label: #74431c;
--flxn-showcase-bg: #f6efe0;
--flxn-overlay: rgba(26, 18, 10, 0.45);
--flxn-dotgrid-color: rgba(138, 104, 58, 0.22);
}
/* ---------------------------------------------------------------- *
* 3. Full-page decoration: candlelit vignette (dark scheme only)
* ---------------------------------------------------------------- */
[data-theme="gothic"][data-mantine-color-scheme="dark"] body::before {
content: "";
position: fixed;
inset: 0;
z-index: 9998;
pointer-events: none;
background:
radial-gradient(80% 50% at 50% 0%, rgba(176, 141, 87, 0.04), transparent 70%),
radial-gradient(120% 90% at 50% 42%, transparent 55%, rgba(4, 2, 6, 0.45) 100%);
}
/* ---------------------------------------------------------------- *
* 4. Component restyles
* ---------------------------------------------------------------- */
/* Headings: Cormorant, slight italic from h3 down for the manuscript voice. */
[data-theme="gothic"] .mantine-Title-root {
letter-spacing: 0.01em;
}
[data-theme="gothic"] h3.mantine-Title-root,
[data-theme="gothic"] h4.mantine-Title-root {
font-style: italic;
font-weight: 600;
}
/* Cards & panels: thin double border in brass (1px border + inset outline). */
[data-theme="gothic"] .mantine-Paper-root[data-with-border] {
border: 1px solid var(--gothic-brass-faint);
outline: 1px solid var(--gothic-brass-ghost);
outline-offset: -4px;
background-color: var(--gothic-surface);
}
[data-theme="gothic"] .mantine-Card-root {
background-color: var(--gothic-surface);
}
/* Buttons: wax-seal solids in the user's accent, or brass-ruled outlines. */
[data-theme="gothic"] .mantine-Button-root {
letter-spacing: 0.03em;
}
[data-theme="gothic"] .mantine-Button-root[data-variant="filled"],
[data-theme="gothic"] .mantine-ActionIcon-root[data-variant="filled"] {
border: 1px solid var(--mantine-primary-color-9);
box-shadow: inset 0 1px 0 rgba(246, 239, 224, 0.12), 0 2px 8px rgba(0, 0, 0, 0.45);
}
[data-theme="gothic"] .mantine-Button-root[data-variant="default"],
[data-theme="gothic"] .mantine-Button-root[data-variant="outline"],
[data-theme="gothic"] .mantine-ActionIcon-root[data-variant="default"],
[data-theme="gothic"] .mantine-ActionIcon-root[data-variant="outline"] {
border-color: var(--gothic-brass-faint);
}
/* Inputs: recessed ledger lines with brass rules; focus follows the accent. */
[data-theme="gothic"] .mantine-Input-input {
background-color: var(--gothic-recessed);
border-color: var(--gothic-brass-faint);
}
[data-theme="gothic"] .mantine-PinInput-input {
background-color: var(--gothic-recessed);
}
/* Tabs: rubricated active label in the accent under a brass rule. */
[data-theme="gothic"] .mantine-Tabs-list {
--tabs-list-border-size: 1px;
}
[data-theme="gothic"] .mantine-Tabs-tab {
letter-spacing: 0.02em;
}
[data-theme="gothic"] .mantine-Tabs-tab[data-active] {
border-color: var(--gothic-brass);
color: var(--mantine-primary-color-light-color);
}
[data-theme="gothic"] .mantine-Tabs-tab[data-active]:hover {
border-color: var(--gothic-brass-bright);
}
/* Nav: active entries follow the accent — tinted wash plus a marginal rule. */
[data-theme="gothic"] .mantine-NavLink-root[data-active] {
background-color: var(--mantine-primary-color-light);
color: var(--mantine-primary-color-light-color);
box-shadow: inset 2px 0 0 var(--mantine-primary-color-filled);
}
[data-theme="gothic"] .mantine-NavLink-root[data-active]:hover {
background-color: var(--mantine-primary-color-light-hover);
}
/* Shell: bound pages — header and navbar as darker leather edges. */
[data-theme="gothic"] .mantine-AppShell-header {
background-color: var(--gothic-shell);
border-bottom: 1px solid var(--gothic-brass-faint);
}
[data-theme="gothic"] .mantine-AppShell-navbar {
background-color: var(--gothic-shell);
border-color: var(--gothic-brass-faint);
}
/* Segmented control: engraved channel with a brass-edged slider. */
[data-theme="gothic"] .mantine-SegmentedControl-root {
background-color: var(--gothic-recessed);
}
[data-theme="gothic"] .mantine-SegmentedControl-indicator {
background-color: var(--gothic-surface);
border: 1px solid var(--gothic-brass-faint);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
}
[data-theme="gothic"] .mantine-SegmentedControl-label {
letter-spacing: 0.02em;
}
/* Dividers: engraved rule with a centered fleuron ornament. */
[data-theme="gothic"] .mantine-Divider-root {
--divider-color: var(--gothic-line);
}
[data-theme="gothic"] .mantine-Divider-root[data-orientation="horizontal"] {
box-shadow: 0 1px 0 var(--gothic-line-highlight);
}
[data-theme="gothic"] .mantine-Divider-root[data-orientation="horizontal"]:not([data-with-label]) {
position: relative;
overflow: visible;
}
[data-theme="gothic"] .mantine-Divider-root[data-orientation="horizontal"]:not([data-with-label])::before {
content: "\2766"; /* ❦ fleuron */
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding: 0 0.5em;
background-color: var(--mantine-color-body);
color: var(--gothic-brass);
font-size: 0.8rem;
line-height: 1;
}
[data-theme="gothic"] .mantine-Divider-label {
color: var(--gothic-brass);
letter-spacing: 0.08em;
text-transform: uppercase;
font-size: var(--mantine-font-size-xs);
}
/* Overlays: sealed-note dropdowns and modals with double brass borders. */
[data-theme="gothic"] .mantine-Modal-content,
[data-theme="gothic"] .mantine-Popover-dropdown {
background-color: var(--gothic-surface);
border: 1px solid var(--gothic-brass-faint);
outline: 1px solid var(--gothic-brass-ghost);
outline-offset: -4px;
}
[data-theme="gothic"] .mantine-Modal-header {
background-color: var(--gothic-surface);
}
[data-theme="gothic"] .mantine-Tooltip-tooltip {
background-color: #060409;
color: #e8e0d0;
border: 1px solid var(--gothic-brass-faint);
}
/* Notifications: sealed notes with a brass frame. */
[data-theme="gothic"] .mantine-Notification-root {
background-color: var(--gothic-surface);
border: 1px solid var(--gothic-brass-faint);
box-shadow: var(--mantine-shadow-md);
}
/* Badges: engraved plaques. */
[data-theme="gothic"] .mantine-Badge-root {
letter-spacing: 0.06em;
font-weight: 600;
}
/* Alerts keep a real border for forced-colors resilience. */
[data-theme="gothic"] .mantine-Alert-root {
border: 1px solid var(--gothic-brass-faint);
}
/* ---------------------------------------------------------------- *
* 5. App component hooks
* ---------------------------------------------------------------- */
/* Tab strip: docks as a shell strip with a brass rule; active label
rubricated in the accent over a thin double brass rule (gilded underline). */
[data-theme="gothic"] .flxn-tabs[data-stuck] {
background-color: var(--gothic-shell);
box-shadow: inset 0 -1px 0 var(--gothic-brass-faint);
}
[data-theme="gothic"] .flxn-tab {
color: var(--mantine-color-dimmed);
letter-spacing: 0.02em;
}
[data-theme="gothic"] .flxn-tab[data-active] {
color: var(--mantine-primary-color-light-color);
font-weight: 600;
}
[data-theme="gothic"] .flxn-tabs-indicator {
border-bottom: 3px double var(--gothic-brass);
padding-inline: 0.5rem;
}
/* Nav grid tiles: brass double-ruled panels, flat gilded glyph. */
[data-theme="gothic"] .flxn-tile {
background-color: var(--gothic-surface);
border: 1px solid var(--gothic-brass-faint);
outline: 1px solid var(--gothic-brass-ghost);
outline-offset: -4px;
}
[data-theme="gothic"] .flxn-tile-icon {
color: var(--gothic-brass-bright);
}
/* "See all" link: brass-rimmed chip, accent-inked label. */
[data-theme="gothic"] .flxn-see-all {
background-color: var(--gothic-brass-ghost);
border: 1px solid var(--gothic-brass-faint);
color: var(--mantine-primary-color-light-color);
}
@media (hover: hover) {
[data-theme="gothic"] .flxn-see-all:hover {
background-color: var(--mantine-primary-color-light);
}
}
/* Match card + reaction tray: one construction — matching brass rules,
tray recessed as a deeper ink panel. */
[data-theme="gothic"] .flxn-match-card {
background-color: var(--gothic-surface);
border: 1px solid var(--gothic-brass-faint);
}
[data-theme="gothic"] .flxn-reaction-row {
background-color: var(--gothic-recessed);
border: 1px solid var(--gothic-brass-faint);
}
+69
View File
@@ -0,0 +1,69 @@
/* Default look for custom app-component hooks; themes override under
[data-theme] scopes (see README "App component hooks"). Loads before all
theme.css files. */
/* Mantine hardcodes white on selected combobox options; autoContrast never
reaches this rule. */
[data-combobox-selected] {
color: var(--flxn-on-accent);
}
/* Swipeable tab strip: flat while resting, docked chrome on [data-stuck]. */
.flxn-tabs {
background-color: transparent;
border-bottom: 1px solid transparent;
transition: background-color 200ms ease, border-color 200ms ease,
box-shadow 200ms ease;
}
.flxn-tabs[data-stuck] {
background-color: var(--mantine-color-body);
border-bottom-color: var(--mantine-color-default-border);
}
@media (prefers-reduced-motion: reduce) {
.flxn-tabs {
transition: none;
}
}
.flxn-tab.flxn-tab {
/* lifted above the absolutely-positioned FloatingIndicator, or an opaque
indicator fill covers the labels */
position: relative;
z-index: 1;
flex: 1;
padding: var(--mantine-spacing-sm) var(--mantine-spacing-xs);
text-align: center;
color: var(--mantine-color-text);
font-weight: 400;
background-color: transparent;
border: none;
border-radius: 0;
transition: color 200ms ease;
}
.flxn-tab.flxn-tab[data-active] {
color: var(--mantine-primary-color-filled);
font-weight: 600;
}
.flxn-tabs-indicator.flxn-tabs-indicator {
border-bottom: 2px solid var(--mantine-primary-color-filled);
padding-inline: 0.5rem;
}
@media (prefers-reduced-motion: reduce) {
.flxn-tab.flxn-tab {
transition: none;
}
}
/* The frame owns the avatar's single border (--flxn-avatar-border); an inner
Avatar border would double every ring. */
.flxn-avatar-frame .mantine-Avatar-root.mantine-Avatar-root {
border: none;
outline: none;
}
/* Reaction tray tucked under a match card; full border + radius so rounded
card corners never expose square tray corners. */
.flxn-reaction-row.flxn-reaction-row {
background-color: var(--mantine-color-gray-light);
border: 1px solid var(--mantine-color-default-border);
border-radius: var(--mantine-radius-md);
}
+17
View File
@@ -0,0 +1,17 @@
import { msg } from "@lingui/core/macro";
import type { ThemeManifest } from "../types";
const manifest: ThemeManifest = {
id: "neumorph",
label: msg`Neumorphic`,
colorSchemes: "both",
fontLinks: [
"https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700;800&display=swap",
],
chromeColors: {
light: { base: "#e0e5ec", dimmed: "#c8d0dc" },
dark: { base: "#2a2d34", dimmed: "#1e2127" },
},
};
export default manifest;
+632
View File
@@ -0,0 +1,632 @@
/* Neumorphic — soft-UI extruded plastic. One surface color for body AND
cards; elevation comes only from paired light/dark shadows. Raised =
drop pair, pressed = inset pair. Faint real 1px borders stay on every
interactive element so forced-colors / high-contrast modes survive. */
/* ---------------------------------------------------------------- */
/* 1. Scheme-independent tokens */
/* ---------------------------------------------------------------- */
[data-theme="neumorph"][data-theme] {
--mantine-font-family: "Nunito", "Inter", system-ui, sans-serif;
--mantine-font-family-headings: "Nunito", "Inter", system-ui, sans-serif;
--mantine-heading-font-weight: 800;
--mantine-radius-xs: 8px;
--mantine-radius-sm: 12px;
--mantine-radius-md: 16px;
--mantine-radius-lg: 20px;
--mantine-radius-xl: 28px;
--mantine-radius-default: 16px;
--flxn-avatar-radius: 30%;
--flxn-avatar-border: 1px solid var(--neu-border);
--flxn-badge-radius: 16px;
/* muted, low-saturation slate grays (light-scheme neutrals) */
--mantine-color-gray-0: #f2f5fa;
--mantine-color-gray-1: #e9edf4;
--mantine-color-gray-2: #dfe4ed;
--mantine-color-gray-3: #d0d7e2;
--mantine-color-gray-4: #bac4d3;
--mantine-color-gray-5: #9daabd;
--mantine-color-gray-6: #7d8ba0;
--mantine-color-gray-7: #606d81;
--mantine-color-gray-8: #495466;
--mantine-color-gray-9: #333c4b;
/* dark-scheme neutrals tuned to the #2a2d34 shell */
--mantine-color-dark-0: #d5dae3;
--mantine-color-dark-1: #b8bfcb;
--mantine-color-dark-2: #99a1b0;
--mantine-color-dark-3: #6f7787;
--mantine-color-dark-4: #4a505c;
--mantine-color-dark-5: #383d47;
--mantine-color-dark-6: #2a2d34;
--mantine-color-dark-7: #2a2d34;
--mantine-color-dark-8: #222429;
--mantine-color-dark-9: #191b1f;
/* accent ramps retuned to muted pastel-plastic; shade 6 keeps >=4.5:1 on
the light surface, shades 3-4 keep >=4.5:1 on the dark surface */
--mantine-color-blue-0: #e9eff6;
--mantine-color-blue-1: #d3dfee;
--mantine-color-blue-2: #b9cce4;
--mantine-color-blue-3: #98b4d7;
--mantine-color-blue-4: #779cca;
--mantine-color-blue-5: #5181bb;
--mantine-color-blue-6: #3c669a;
--mantine-color-blue-7: #31537d;
--mantine-color-blue-8: #274263;
--mantine-color-blue-9: #1d3149;
--mantine-color-red-0: #f6ebe9;
--mantine-color-red-1: #edd6d4;
--mantine-color-red-2: #e3beba;
--mantine-color-red-3: #d6a09a;
--mantine-color-red-4: #c88179;
--mantine-color-red-5: #b95d53;
--mantine-color-red-6: #98473e;
--mantine-color-red-7: #7b3a32;
--mantine-color-red-8: #622e28;
--mantine-color-red-9: #48221e;
--mantine-color-green-0: #eaf5f0;
--mantine-color-green-1: #d5ebe0;
--mantine-color-green-2: #bde0cf;
--mantine-color-green-3: #9ed1b8;
--mantine-color-green-4: #7fc3a1;
--mantine-color-green-5: #4ea67a;
--mantine-color-green-6: #357153;
--mantine-color-green-7: #2b5c44;
--mantine-color-green-8: #214734;
--mantine-color-green-9: #183225;
--mantine-color-yellow-0: #f9f4e7;
--mantine-color-yellow-1: #f2e9cf;
--mantine-color-yellow-2: #ebdcb2;
--mantine-color-yellow-3: #e1cb8e;
--mantine-color-yellow-4: #d7ba6a;
--mantine-color-yellow-5: #bc9732;
--mantine-color-yellow-6: #7b6321;
--mantine-color-yellow-7: #634f1a;
--mantine-color-yellow-8: #4b3c14;
--mantine-color-yellow-9: #32290d;
--mantine-color-grape-0: #f3eaf5;
--mantine-color-grape-1: #e7d5eb;
--mantine-color-grape-2: #d9bde0;
--mantine-color-grape-3: #c79ed1;
--mantine-color-grape-4: #b680c3;
--mantine-color-grape-5: #a15bb2;
--mantine-color-grape-6: #824592;
--mantine-color-grape-7: #693776;
--mantine-color-grape-8: #542c5e;
--mantine-color-grape-9: #3e2145;
--mantine-color-orange-0: #f9efe7;
--mantine-color-orange-1: #f2decf;
--mantine-color-orange-2: #ebcbb2;
--mantine-color-orange-3: #e1b28e;
--mantine-color-orange-4: #d7996a;
--mantine-color-orange-5: #c97636;
--mantine-color-orange-6: #955728;
--mantine-color-orange-7: #7d4921;
--mantine-color-orange-8: #653b1b;
--mantine-color-orange-9: #4d2d14;
--mantine-color-pink-0: #f6e9ee;
--mantine-color-pink-1: #edd4dc;
--mantine-color-pink-2: #e3bac8;
--mantine-color-pink-3: #d69aae;
--mantine-color-pink-4: #ca7d96;
--mantine-color-pink-5: #b95576;
--mantine-color-pink-6: #983e5c;
--mantine-color-pink-7: #7b324b;
--mantine-color-pink-8: #62283b;
--mantine-color-pink-9: #481e2c;
--mantine-color-lime-0: #eff6e9;
--mantine-color-lime-1: #e0eed3;
--mantine-color-lime-2: #cde4b9;
--mantine-color-lime-3: #b6d798;
--mantine-color-lime-4: #9eca77;
--mantine-color-lime-5: #74ac43;
--mantine-color-lime-6: #4b702c;
--mantine-color-lime-7: #3d5a23;
--mantine-color-lime-8: #2e441a;
--mantine-color-lime-9: #1f2e12;
}
/* ---------------------------------------------------------------- */
/* 2. Per-scheme tokens */
/* ---------------------------------------------------------------- */
[data-theme="neumorph"][data-theme][data-mantine-color-scheme="light"] {
--neu-surface: #e0e5ec;
--neu-surface-high: #e8ecf2; /* hover */
--neu-well: #d3dae4; /* pressed-in wells */
--neu-border: rgba(148, 163, 184, 0.4);
--neu-shadow-dark: rgba(163, 177, 198, 0.6);
--neu-shadow-light: rgba(255, 255, 255, 0.9);
--neu-inset: inset 6px 6px 12px rgba(163, 177, 198, 0.6),
inset -6px -6px 12px rgba(255, 255, 255, 0.9);
--neu-inset-sm: inset 3px 3px 6px rgba(163, 177, 198, 0.55),
inset -3px -3px 6px rgba(255, 255, 255, 0.85);
--neu-edge: 0 8px 16px rgba(163, 177, 198, 0.35);
--mantine-color-body: #e0e5ec;
--mantine-color-text: #2f3542;
--mantine-color-bright: #171b23;
--mantine-color-dimmed: #5b6472;
--mantine-color-placeholder: #5f6a80;
--mantine-color-error: var(--mantine-color-red-6);
--mantine-color-default: #e0e5ec;
--mantine-color-default-hover: #e8ecf2;
--mantine-color-default-color: #2f3542;
--mantine-color-default-border: rgba(148, 163, 184, 0.4);
/* Mantine's -light variants are alpha literals of its stock hues;
re-derive them from the retuned ramps */
--mantine-color-blue-light: color-mix(in srgb, var(--mantine-color-blue-6) 10%, transparent);
--mantine-color-blue-light-hover: color-mix(in srgb, var(--mantine-color-blue-6) 14%, transparent);
--mantine-color-red-light: color-mix(in srgb, var(--mantine-color-red-6) 10%, transparent);
--mantine-color-red-light-hover: color-mix(in srgb, var(--mantine-color-red-6) 14%, transparent);
--mantine-color-green-light: color-mix(in srgb, var(--mantine-color-green-6) 10%, transparent);
--mantine-color-green-light-hover: color-mix(in srgb, var(--mantine-color-green-6) 14%, transparent);
--mantine-color-yellow-light: color-mix(in srgb, var(--mantine-color-yellow-6) 10%, transparent);
--mantine-color-yellow-light-hover: color-mix(in srgb, var(--mantine-color-yellow-6) 14%, transparent);
--mantine-color-grape-light: color-mix(in srgb, var(--mantine-color-grape-6) 10%, transparent);
--mantine-color-grape-light-hover: color-mix(in srgb, var(--mantine-color-grape-6) 14%, transparent);
--mantine-color-orange-light: color-mix(in srgb, var(--mantine-color-orange-6) 10%, transparent);
--mantine-color-orange-light-hover: color-mix(in srgb, var(--mantine-color-orange-6) 14%, transparent);
--mantine-color-pink-light: color-mix(in srgb, var(--mantine-color-pink-6) 10%, transparent);
--mantine-color-pink-light-hover: color-mix(in srgb, var(--mantine-color-pink-6) 14%, transparent);
--mantine-color-lime-light: color-mix(in srgb, var(--mantine-color-lime-6) 10%, transparent);
--mantine-color-lime-light-hover: color-mix(in srgb, var(--mantine-color-lime-6) 14%, transparent);
--mantine-color-gray-light: color-mix(in srgb, var(--mantine-color-gray-6) 10%, transparent);
--mantine-color-gray-light-hover: color-mix(in srgb, var(--mantine-color-gray-6) 14%, transparent);
/* Compact spreads: app containers clip overflow, and big soft shadows
truncate visibly at those edges. Keep the extrusion read tight. */
--mantine-shadow-xs: 2px 2px 5px rgba(163, 177, 198, 0.45),
-2px -2px 5px rgba(255, 255, 255, 0.8);
--mantine-shadow-sm: 3px 3px 7px rgba(163, 177, 198, 0.5),
-3px -3px 7px rgba(255, 255, 255, 0.85);
--mantine-shadow-md: 4px 4px 9px rgba(163, 177, 198, 0.55),
-4px -4px 9px rgba(255, 255, 255, 0.85);
--mantine-shadow-lg: 5px 5px 12px rgba(163, 177, 198, 0.55),
-5px -5px 12px rgba(255, 255, 255, 0.85);
--mantine-shadow-xl: 7px 7px 16px rgba(163, 177, 198, 0.6),
-7px -7px 16px rgba(255, 255, 255, 0.9);
/* app tokens — soft embossed metals, muted toasts, extruded podium */
--flxn-gold: #8f6f1e;
--flxn-silver: #66727f;
--flxn-bronze: #96603a;
--flxn-toast-success: #2f8f5b;
--flxn-toast-error: #b04a44;
--flxn-podium-1-bg: color-mix(in srgb, #d9b25a 10%, #e0e5ec);
--flxn-podium-1-border: color-mix(in srgb, #b08d2c 35%, rgba(148, 163, 184, 0.5));
--flxn-podium-1-label: #7d651f;
--flxn-podium-2-bg: color-mix(in srgb, #9daabd 10%, #e0e5ec);
--flxn-podium-2-border: color-mix(in srgb, #7d8ba0 35%, rgba(148, 163, 184, 0.5));
--flxn-podium-2-label: #5b6472;
--flxn-podium-3-bg: color-mix(in srgb, #c08552 10%, #e0e5ec);
--flxn-podium-3-border: color-mix(in srgb, #a56a3a 35%, rgba(148, 163, 184, 0.5));
--flxn-podium-3-label: #7d5230;
--flxn-showcase-bg: #e0e5ec;
--flxn-overlay: rgba(99, 112, 133, 0.45);
--flxn-dotgrid-color: rgba(148, 163, 184, 0.5);
--flxn-pop-shadow: rgba(163, 177, 198, 0.65);
}
[data-theme="neumorph"][data-theme][data-mantine-color-scheme="dark"] {
--neu-surface: #2a2d34;
--neu-surface-high: #31353d;
--neu-well: #23262c;
--neu-border: rgba(255, 255, 255, 0.08);
--neu-shadow-dark: rgba(13, 15, 19, 0.65);
--neu-shadow-light: rgba(68, 74, 86, 0.5);
--neu-inset: inset 6px 6px 12px rgba(13, 15, 19, 0.65),
inset -6px -6px 12px rgba(68, 74, 86, 0.5);
--neu-inset-sm: inset 3px 3px 6px rgba(13, 15, 19, 0.6),
inset -3px -3px 6px rgba(68, 74, 86, 0.45);
--neu-edge: 0 8px 16px rgba(0, 0, 0, 0.4);
--mantine-color-body: #2a2d34;
--mantine-color-text: #dbdfe7;
--mantine-color-bright: #f4f6fa;
--mantine-color-dimmed: #9ba3b2;
--mantine-color-placeholder: #8a92a3;
--mantine-color-error: var(--mantine-color-red-3);
--mantine-color-default: #2a2d34;
--mantine-color-default-hover: #31353d;
--mantine-color-default-color: #dbdfe7;
--mantine-color-default-border: rgba(255, 255, 255, 0.08);
/* re-derive alpha-literal -light variants from the retuned ramps */
--mantine-color-blue-light: color-mix(in srgb, var(--mantine-color-blue-6) 15%, transparent);
--mantine-color-blue-light-hover: color-mix(in srgb, var(--mantine-color-blue-6) 20%, transparent);
--mantine-color-red-light: color-mix(in srgb, var(--mantine-color-red-6) 15%, transparent);
--mantine-color-red-light-hover: color-mix(in srgb, var(--mantine-color-red-6) 20%, transparent);
--mantine-color-green-light: color-mix(in srgb, var(--mantine-color-green-6) 15%, transparent);
--mantine-color-green-light-hover: color-mix(in srgb, var(--mantine-color-green-6) 20%, transparent);
--mantine-color-yellow-light: color-mix(in srgb, var(--mantine-color-yellow-6) 15%, transparent);
--mantine-color-yellow-light-hover: color-mix(in srgb, var(--mantine-color-yellow-6) 20%, transparent);
--mantine-color-grape-light: color-mix(in srgb, var(--mantine-color-grape-6) 15%, transparent);
--mantine-color-grape-light-hover: color-mix(in srgb, var(--mantine-color-grape-6) 20%, transparent);
--mantine-color-orange-light: color-mix(in srgb, var(--mantine-color-orange-6) 15%, transparent);
--mantine-color-orange-light-hover: color-mix(in srgb, var(--mantine-color-orange-6) 20%, transparent);
--mantine-color-pink-light: color-mix(in srgb, var(--mantine-color-pink-6) 15%, transparent);
--mantine-color-pink-light-hover: color-mix(in srgb, var(--mantine-color-pink-6) 20%, transparent);
--mantine-color-lime-light: color-mix(in srgb, var(--mantine-color-lime-6) 15%, transparent);
--mantine-color-lime-light-hover: color-mix(in srgb, var(--mantine-color-lime-6) 20%, transparent);
--mantine-color-gray-light: color-mix(in srgb, var(--mantine-color-gray-5) 15%, transparent);
--mantine-color-gray-light-hover: color-mix(in srgb, var(--mantine-color-gray-5) 20%, transparent);
--mantine-shadow-xs: 2px 2px 5px rgba(13, 15, 19, 0.5),
-2px -2px 5px rgba(68, 74, 86, 0.4);
--mantine-shadow-sm: 3px 3px 7px rgba(13, 15, 19, 0.55),
-3px -3px 7px rgba(68, 74, 86, 0.45);
--mantine-shadow-md: 4px 4px 9px rgba(13, 15, 19, 0.6),
-4px -4px 9px rgba(68, 74, 86, 0.45);
--mantine-shadow-lg: 5px 5px 12px rgba(13, 15, 19, 0.6),
-5px -5px 12px rgba(68, 74, 86, 0.45);
--mantine-shadow-xl: 7px 7px 16px rgba(13, 15, 19, 0.65),
-7px -7px 16px rgba(68, 74, 86, 0.5);
--flxn-gold: #dcbb6a;
--flxn-silver: #b3bcc9;
--flxn-bronze: #cf9a72;
--flxn-toast-success: #86d3a8;
--flxn-toast-error: #ef9d9d;
--flxn-podium-1-bg: color-mix(in srgb, #dcbb6a 12%, #2a2d34);
--flxn-podium-1-border: color-mix(in srgb, #dcbb6a 30%, rgba(255, 255, 255, 0.12));
--flxn-podium-1-label: #dcbb6a;
--flxn-podium-2-bg: color-mix(in srgb, #b3bcc9 10%, #2a2d34);
--flxn-podium-2-border: color-mix(in srgb, #b3bcc9 28%, rgba(255, 255, 255, 0.12));
--flxn-podium-2-label: #b3bcc9;
--flxn-podium-3-bg: color-mix(in srgb, #cf9a72 11%, #2a2d34);
--flxn-podium-3-border: color-mix(in srgb, #cf9a72 28%, rgba(255, 255, 255, 0.12));
--flxn-podium-3-label: #cf9a72;
--flxn-showcase-bg: #2a2d34;
--flxn-overlay: rgba(0, 0, 0, 0.55);
--flxn-dotgrid-color: rgba(255, 255, 255, 0.09);
--flxn-pop-shadow: rgba(0, 0, 0, 0.6);
}
/* ---------------------------------------------------------------- */
/* 3. Surfaces — same color as body, elevation via shadow only */
/* ---------------------------------------------------------------- */
[data-theme="neumorph"] .mantine-Paper-root {
background-color: var(--neu-surface);
border: 1px solid var(--neu-border);
border-radius: var(--mantine-radius-md);
box-shadow: var(--mantine-shadow-md);
}
[data-theme="neumorph"] .mantine-Card-root {
border-radius: var(--mantine-radius-lg);
}
/* Floating surfaces sit higher */
[data-theme="neumorph"] .mantine-Modal-content,
[data-theme="neumorph"] .mantine-Popover-dropdown,
[data-theme="neumorph"] .mantine-Notification-root {
background-color: var(--neu-surface);
border: 1px solid var(--neu-border);
box-shadow: var(--mantine-shadow-lg);
}
[data-theme="neumorph"] .mantine-Modal-content {
border-radius: var(--mantine-radius-lg);
}
[data-theme="neumorph"] .mantine-Modal-header {
background-color: var(--neu-surface);
}
[data-theme="neumorph"] .mantine-Notification-root {
border-radius: var(--mantine-radius-md);
}
[data-theme="neumorph"] .mantine-Tooltip-tooltip {
border-radius: var(--mantine-radius-sm);
box-shadow: var(--mantine-shadow-sm);
}
[data-theme="neumorph"] .mantine-Alert-root {
border-radius: var(--mantine-radius-md);
box-shadow: var(--mantine-shadow-xs);
}
/* App shell: soft edges instead of hard hairlines */
[data-theme="neumorph"] .mantine-AppShell-header {
background-color: var(--neu-surface);
border-bottom: 1px solid var(--neu-border);
box-shadow: var(--neu-edge);
}
[data-theme="neumorph"] .mantine-AppShell-navbar {
background-color: var(--neu-surface);
border-top: 1px solid var(--neu-border);
border-inline-end: 1px solid var(--neu-border);
box-shadow: var(--neu-edge);
}
/* ---------------------------------------------------------------- */
/* 4. Raised controls — extruded plastic; press swaps to inset */
/* ---------------------------------------------------------------- */
[data-theme="neumorph"] .mantine-Button-root,
[data-theme="neumorph"] .mantine-ActionIcon-root {
border: 1px solid var(--neu-border);
border-radius: var(--mantine-radius-md);
box-shadow: var(--mantine-shadow-sm);
transition:
box-shadow 160ms ease,
transform 120ms cubic-bezier(0.32, 0.72, 0, 1);
}
[data-theme="neumorph"] .mantine-Button-root:where([data-variant="default"], [data-variant="outline"]),
[data-theme="neumorph"] .mantine-ActionIcon-root:where([data-variant="default"], [data-variant="outline"]) {
background-color: var(--neu-surface);
color: var(--mantine-color-default-color);
}
[data-theme="neumorph"] .mantine-Button-root:where([data-variant="default"], [data-variant="outline"]):hover,
[data-theme="neumorph"] .mantine-ActionIcon-root:where([data-variant="default"], [data-variant="outline"]):hover {
background-color: var(--neu-surface-high);
}
/* Ghost variants stay flat — no floating shadow plates */
[data-theme="neumorph"] .mantine-Button-root:where([data-variant="subtle"], [data-variant="transparent"], [data-variant="white"]),
[data-theme="neumorph"] .mantine-ActionIcon-root:where([data-variant="subtle"], [data-variant="transparent"], [data-variant="white"]) {
border-color: transparent;
box-shadow: none;
}
[data-theme="neumorph"] .mantine-Button-root:active:not([data-disabled], :disabled, [data-loading]),
[data-theme="neumorph"] .mantine-ActionIcon-root:active:not([data-disabled], :disabled, [data-loading]) {
box-shadow: var(--neu-inset-sm);
}
[data-theme="neumorph"] .mantine-Button-root:where([data-disabled], :disabled),
[data-theme="neumorph"] .mantine-ActionIcon-root:where([data-disabled], :disabled) {
box-shadow: none;
}
/* Re-flavor global press feedback: pressed-in, not just scaled */
@media (prefers-reduced-motion: no-preference) {
[data-theme="neumorph"] .flxn-press:active:not(:disabled):not([data-disabled]):not([data-loading]) {
box-shadow: var(--neu-inset-sm);
transform: scale(0.985);
}
}
/* ---------------------------------------------------------------- */
/* 5. Inputs — pressed-in wells */
/* ---------------------------------------------------------------- */
[data-theme="neumorph"] .mantine-Input-input {
background-color: var(--neu-surface);
border: 1px solid var(--neu-border);
border-radius: var(--mantine-radius-md);
box-shadow: var(--neu-inset-sm);
transition: border-color 160ms ease, box-shadow 160ms ease;
}
[data-theme="neumorph"] .mantine-Input-input:focus,
[data-theme="neumorph"] .mantine-Input-input:focus-within {
border-color: var(--mantine-primary-color-filled);
box-shadow: var(--neu-inset);
}
[data-theme="neumorph"] .mantine-Input-input[data-error] {
border-color: var(--mantine-color-error);
}
/* ---------------------------------------------------------------- */
/* 6. Selection controls */
/* ---------------------------------------------------------------- */
/* SegmentedControl: raised track, indicator is a pressed-in well */
[data-theme="neumorph"] .mantine-SegmentedControl-root {
background-color: var(--neu-surface);
border: 1px solid var(--neu-border);
border-radius: var(--mantine-radius-md);
box-shadow: var(--mantine-shadow-xs);
}
[data-theme="neumorph"] .mantine-SegmentedControl-indicator {
background-color: var(--neu-well);
border: 1px solid var(--neu-border);
border-radius: var(--mantine-radius-sm);
box-shadow: var(--neu-inset-sm);
}
[data-theme="neumorph"] .mantine-SegmentedControl-label[data-active] {
color: var(--mantine-color-bright);
}
/* Switch: inset track, raised thumb */
[data-theme="neumorph"] .mantine-Switch-track {
border: 1px solid var(--neu-border);
box-shadow: var(--neu-inset-sm);
}
[data-theme="neumorph"] .mantine-Switch-thumb {
border: 1px solid var(--neu-border);
box-shadow: 2px 2px 5px var(--neu-shadow-dark), -2px -2px 5px var(--neu-shadow-light);
}
/* Checkbox / Radio: unchecked = soft well, checked keeps accent */
[data-theme="neumorph"] .mantine-Checkbox-input,
[data-theme="neumorph"] .mantine-Radio-radio {
border: 1px solid var(--neu-border);
}
[data-theme="neumorph"] .mantine-Checkbox-input:not(:checked),
[data-theme="neumorph"] .mantine-Radio-radio:not(:checked) {
background-color: var(--neu-surface);
box-shadow: var(--neu-inset-sm);
}
[data-theme="neumorph"] .mantine-Checkbox-input {
border-radius: 0.375rem;
}
/* Progress: inset trough, soft bar */
[data-theme="neumorph"] .mantine-Progress-root {
background-color: var(--neu-well);
border: 1px solid var(--neu-border);
box-shadow: var(--neu-inset-sm);
}
[data-theme="neumorph"] .mantine-Progress-section {
border-radius: var(--mantine-radius-xl);
}
/* ---------------------------------------------------------------- */
/* 7. Navigation & misc */
/* ---------------------------------------------------------------- */
[data-theme="neumorph"] .mantine-Tabs-tab {
border-radius: var(--mantine-radius-sm) var(--mantine-radius-sm) 0 0;
}
[data-theme="neumorph"] .mantine-Tabs-tab:hover {
background-color: var(--neu-surface-high);
}
[data-theme="neumorph"] .mantine-NavLink-root {
border: 1px solid transparent;
border-radius: var(--mantine-radius-md);
}
[data-theme="neumorph"] .mantine-NavLink-root[data-active] {
background-color: var(--neu-well);
border-color: var(--neu-border);
box-shadow: var(--neu-inset-sm);
color: var(--mantine-color-text);
}
[data-theme="neumorph"] .mantine-Badge-root {
border: 1px solid var(--neu-border);
}
[data-theme="neumorph"] .mantine-Badge-root[data-variant="default"] {
background-color: var(--neu-surface);
box-shadow: var(--mantine-shadow-xs);
color: var(--mantine-color-text);
}
[data-theme="neumorph"] .flxn-avatar-frame {
box-shadow: var(--mantine-shadow-xs);
}
[data-theme="neumorph"] .mantine-ThemeIcon-root[data-variant="default"] {
background-color: var(--neu-surface);
border: 1px solid var(--neu-border);
box-shadow: var(--mantine-shadow-xs);
}
[data-theme="neumorph"] .mantine-Skeleton-root {
border-radius: var(--mantine-radius-md);
}
[data-theme="neumorph"] .mantine-Divider-root {
--divider-color: var(--neu-border);
}
/* ---------------------------------------------------------------- */
/* 8. App hooks — tab strip, nav tiles, chips, match tray */
/* ---------------------------------------------------------------- */
/* Tab strip: flat while resting; docking lands it back on the shared
surface. The active state is a soft pressed-in pill instead of a hard
underline, legible on the bare page (body IS --neu-surface). */
[data-theme="neumorph"] .flxn-tabs[data-stuck] {
background-color: var(--neu-surface);
}
[data-theme="neumorph"] .flxn-tab.flxn-tab {
border-radius: var(--mantine-radius-xl);
}
[data-theme="neumorph"] .flxn-tab.flxn-tab[data-active] {
color: color-mix(
in srgb,
var(--mantine-primary-color-filled) 45%,
var(--mantine-color-text)
);
font-weight: 700;
}
[data-theme="neumorph"] .flxn-tabs-indicator.flxn-tabs-indicator {
border-bottom: none;
border-radius: var(--mantine-radius-xl);
background-color: color-mix(in srgb, var(--mantine-primary-color-filled) 8%, var(--neu-well));
box-shadow: var(--neu-inset-sm);
}
/* Nav tiles: extruded plastic, pressed-in on tap; icon sits on a
debossed coin */
[data-theme="neumorph"] .flxn-tile.flxn-tile {
background-color: var(--neu-surface);
border: 1px solid var(--neu-border);
border-radius: var(--mantine-radius-lg);
box-shadow: var(--mantine-shadow-sm);
}
@media (hover: hover) {
[data-theme="neumorph"] .flxn-tile.flxn-tile:hover:not(:disabled) {
background-color: var(--neu-surface-high);
}
}
[data-theme="neumorph"] .flxn-tile.flxn-tile:active:not(:disabled) {
background-color: var(--neu-surface);
box-shadow: var(--neu-inset-sm);
}
@media (prefers-reduced-motion: no-preference) {
[data-theme="neumorph"] .flxn-tile.flxn-tile {
transition:
background-color 120ms ease-out,
box-shadow 160ms ease,
transform 120ms cubic-bezier(0.32, 0.72, 0, 1);
}
}
[data-theme="neumorph"] .flxn-tile-icon.flxn-tile-icon {
color: light-dark(var(--mantine-primary-color-6), var(--mantine-primary-color-3));
}
/* "See all" as a soft extruded chip */
[data-theme="neumorph"] .flxn-see-all.flxn-see-all {
background-color: var(--neu-surface);
border: 1px solid var(--neu-border);
box-shadow: var(--mantine-shadow-xs);
color: var(--mantine-primary-color-light-color);
}
@media (hover: hover) {
[data-theme="neumorph"] .flxn-see-all.flxn-see-all:hover {
background-color: var(--neu-surface-high);
}
}
[data-theme="neumorph"] .flxn-see-all.flxn-see-all:active {
box-shadow: var(--neu-inset-sm);
}
/* Match card stays extruded; the reaction tray tucked beneath it is a
pressed-in well */
[data-theme="neumorph"] .flxn-match-card.flxn-match-card {
background-color: var(--neu-surface);
border: 1px solid var(--neu-border);
box-shadow: var(--mantine-shadow-md);
}
[data-theme="neumorph"] .flxn-reaction-row.flxn-reaction-row {
background-color: var(--neu-well);
border: 1px solid var(--neu-border);
border-radius: var(--mantine-radius-md);
box-shadow: var(--neu-inset-sm);
}
+137
View File
@@ -0,0 +1,137 @@
import { msg } from "@lingui/core/macro";
import type { ThemeManifest } from "../types";
/* Bioluminescent sea ramps for all eight accents. Declared in JS (not
theme.css) so Mantine regenerates every derived variant var (-light rgba,
-outline, autoContrast picks) from the retuned shades, whichever accent
the user selects. */
const oceanColors = {
// plankton blue
blue: [
"#e3f7fd",
"#c6eefb",
"#9ce2f8",
"#6dd2f2",
"#45c3ec",
"#27b3e2",
"#1897c7",
"#147ba6",
"#106284",
"#0c4c67",
],
// kelp green
green: [
"#e8f8ee",
"#cdefdb",
"#a8e3bf",
"#80d5a1",
"#5cc686",
"#3eb56e",
"#2f9c5c",
"#27814d",
"#1f683e",
"#185231",
],
// sea-sparkle phosphor
lime: [
"#f2fbe3",
"#e2f6c4",
"#ccee9d",
"#b3e474",
"#9cda50",
"#86ce32",
"#6fb51f",
"#5c9718",
"#4a7a12",
"#3a600e",
],
// siphonophore crimson
red: [
"#fdeef0",
"#fad8dc",
"#f6b6be",
"#f18f9c",
"#ec6c7e",
"#e54d64",
"#d1344e",
"#b02840",
"#8f2034",
"#701929",
],
// anglerfish gold
yellow: [
"#fdf8e2",
"#fbefbe",
"#f8e392",
"#f4d363",
"#efc43c",
"#e8b31d",
"#ce9a10",
"#ad800c",
"#8c6709",
"#6d5007",
],
// amber lure
orange: [
"#fef2e5",
"#fce2c6",
"#f9cb9c",
"#f5b06e",
"#f09746",
"#ea8127",
"#d16a15",
"#af5810",
"#8d460c",
"#6e3709",
],
// medusa violet
grape: [
"#f5f0fd",
"#e8dcfa",
"#d4c0f5",
"#bd9fee",
"#a781e6",
"#9268dc",
"#7e4fca",
"#6a3fae",
"#56328e",
"#43276f",
],
// jelly pink
pink: [
"#fdeef6",
"#fbd8ea",
"#f7b5d8",
"#f28dc2",
"#ec69ae",
"#e54c9c",
"#d13487",
"#b02871",
"#8f215c",
"#701a48",
],
} as const;
const manifest: ThemeManifest = {
id: "ocean",
label: msg`Deep Sea`,
colorSchemes: "both",
fontLinks: [
"https://fonts.googleapis.com/css2?family=Comfortaa:wght@500;600;700&family=Figtree:wght@400;500;600;700&display=swap",
],
chromeColors: {
light: { base: "#eaf6f6", dimmed: "#c3e2df" },
dark: { base: "#052430", dimmed: "#03141d" },
},
mantine: {
// Bright bioluminescent fills in the dark (shade 5), deeper sea tones in
// the shallows (shade 6); bright fills (gold, phosphor, plankton) take
// dark text via autoContrast.
primaryShade: { light: 6, dark: 5 },
autoContrast: true,
luminanceThreshold: 0.3,
colors: oceanColors,
},
};
export default manifest;
+423
View File
@@ -0,0 +1,423 @@
/* Deep Sea: a submarine descent. Dark is the primary mood — a fixed
abyssal deep-teal→ink-navy backdrop with sparse static rising bubbles;
surfaces are opaque pressure-hull panels with 1px bioluminescent rims,
focus states are sonar rings (double accent box-shadow), avatars are
portholes. Light is the sunlit shallows: sand-and-seafoam with a very
subtle refracted-caustic hint. The fixed teal rim/scenery is decoration;
everything interactive derives from --mantine-primary-color-* (the eight
ramps are retuned in manifest.ts as bioluminescent sea colors). No
backdrop-filter anywhere — hulls are solid. */
/* ---------------------------------------------------------------- */
/* 1. Tokens for any scheme */
/* ---------------------------------------------------------------- */
[data-theme="ocean"][data-theme] {
--mantine-font-family: "Figtree", "Avenir Next", "Helvetica Neue", sans-serif;
--mantine-font-family-headings: "Comfortaa", "Figtree", sans-serif;
--mantine-heading-font-weight: 700;
/* rounded, water-worn radii */
--mantine-radius-xs: 0.375rem;
--mantine-radius-sm: 0.5625rem;
--mantine-radius-md: 0.75rem;
--mantine-radius-lg: 1rem;
--mantine-radius-xl: 1.5rem;
--mantine-radius-default: 0.75rem;
/* porthole avatars: circle + bold hull ring (the frame owns the border) */
--flxn-avatar-radius: 50%;
--flxn-avatar-border: 3px solid var(--ocean-porthole);
--flxn-badge-radius: 14px;
/* sonar focus ring: two accent rings, inner solid + outer echo */
--ocean-sonar: 0 0 0 2px
color-mix(in srgb, var(--mantine-primary-color-filled) 90%, transparent),
0 0 0 6px
color-mix(in srgb, var(--mantine-primary-color-filled) 28%, transparent);
}
/* ---------------------------------------------------------------- */
/* 2. Per-scheme tokens */
/* ---------------------------------------------------------------- */
[data-theme="ocean"][data-theme][data-mantine-color-scheme="dark"] {
--mantine-color-body: #062632;
--mantine-color-text: #d9f2f0;
--mantine-color-bright: #f2fffd;
--mantine-color-dimmed: #93bcc0;
--mantine-color-placeholder: #628e96;
--mantine-color-default: #0c3745;
--mantine-color-default-hover: #11424f;
--mantine-color-default-color: #d9f2f0;
--mantine-color-default-border: rgba(103, 232, 214, 0.24);
/* deep-water elevation with a faint bioluminescent edge */
--mantine-shadow-xs: 0 0 0 1px rgba(103, 232, 214, 0.06), 0 2px 6px rgba(1, 10, 16, 0.5);
--mantine-shadow-sm: 0 0 0 1px rgba(103, 232, 214, 0.08), 0 4px 12px rgba(1, 10, 16, 0.55);
--mantine-shadow-md: 0 0 0 1px rgba(103, 232, 214, 0.1), 0 8px 24px rgba(1, 10, 16, 0.6);
--mantine-shadow-lg: 0 0 0 1px rgba(103, 232, 214, 0.12), 0 12px 34px rgba(1, 10, 16, 0.65);
--mantine-shadow-xl: 0 0 0 1px rgba(103, 232, 214, 0.14), 0 18px 48px rgba(1, 10, 16, 0.7);
/* pressure-hull ingredients */
--ocean-hull: #0a3240;
--ocean-hull-raised: #0e3d4d;
--ocean-deep: #041d27;
--ocean-rim: rgba(103, 232, 214, 0.22);
--ocean-rim-strong: rgba(103, 232, 214, 0.38);
--ocean-chrome: #052430;
--ocean-porthole: #2c7482;
--ocean-porthole-glow: 0 0 0 1px rgba(103, 232, 214, 0.25),
0 0 12px rgba(103, 232, 214, 0.2);
--ocean-tooltip-bg: #0d3947;
--ocean-tooltip-color: #dcf5f2;
/* app tokens */
--flxn-gold: #f2c14b;
--flxn-silver: #a9c3cb;
--flxn-bronze: #dd9364;
--flxn-toast-success: #6fe3a5;
--flxn-toast-error: #ff8f9f;
--flxn-showcase-bg: #0a3240;
--flxn-overlay: rgba(2, 10, 16, 0.62);
--flxn-dotgrid-color: rgba(103, 232, 214, 0.16);
--flxn-pop-shadow: rgba(0, 0, 0, 0.55);
/* podium as glowing depth tiers */
--flxn-podium-1-bg: rgba(242, 193, 75, 0.1);
--flxn-podium-1-border: rgba(242, 193, 75, 0.38);
--flxn-podium-1-label: #f2c14b;
--flxn-podium-2-bg: rgba(169, 195, 203, 0.08);
--flxn-podium-2-border: rgba(169, 195, 203, 0.3);
--flxn-podium-2-label: #b8ccd2;
--flxn-podium-3-bg: rgba(221, 147, 100, 0.1);
--flxn-podium-3-border: rgba(221, 147, 100, 0.36);
--flxn-podium-3-label: #e5a273;
}
[data-theme="ocean"][data-theme][data-mantine-color-scheme="light"] {
--mantine-color-body: #eaf6f6;
--mantine-color-text: #11373f;
--mantine-color-bright: #072229;
--mantine-color-dimmed: #4a6e72;
--mantine-color-placeholder: #5f858a;
--mantine-color-default: #ffffff;
--mantine-color-default-hover: #f0f7f4;
--mantine-color-default-color: #11373f;
--mantine-color-default-border: rgba(23, 115, 110, 0.3);
/* soft sea-tinted elevation */
--mantine-shadow-xs: 0 1px 3px rgba(15, 80, 90, 0.1);
--mantine-shadow-sm: 0 2px 8px rgba(15, 80, 90, 0.12);
--mantine-shadow-md: 0 6px 18px rgba(15, 80, 90, 0.14);
--mantine-shadow-lg: 0 10px 28px rgba(15, 80, 90, 0.16);
--mantine-shadow-xl: 0 16px 44px rgba(15, 80, 90, 0.2);
/* sand-and-seafoam ingredients */
--ocean-hull: #fbfaf3;
--ocean-hull-raised: #ffffff;
--ocean-deep: #ddeeea;
--ocean-rim: rgba(23, 115, 110, 0.25);
--ocean-rim-strong: rgba(23, 115, 110, 0.42);
--ocean-chrome: #f3f9f6;
--ocean-porthole: #7fb5ae;
--ocean-porthole-glow: 0 0 0 1px rgba(23, 115, 110, 0.18),
0 2px 8px rgba(15, 80, 90, 0.16);
--ocean-tooltip-bg: #14424d;
--ocean-tooltip-color: #e4f6f3;
/* app tokens */
--flxn-gold: #d8a419;
--flxn-silver: #8ba7ad;
--flxn-bronze: #c76e34;
--flxn-toast-success: #2e9e6b;
--flxn-toast-error: #d64d64;
--flxn-showcase-bg: #ffffff;
--flxn-overlay: rgba(10, 50, 60, 0.4);
--flxn-dotgrid-color: rgba(23, 115, 110, 0.2);
--flxn-pop-shadow: rgba(15, 80, 90, 0.28);
/* podium as sunlit sand tiers */
--flxn-podium-1-bg: rgba(216, 164, 25, 0.14);
--flxn-podium-1-border: rgba(179, 131, 16, 0.45);
--flxn-podium-1-label: #7c5a0b;
--flxn-podium-2-bg: rgba(90, 120, 125, 0.1);
--flxn-podium-2-border: rgba(90, 120, 125, 0.35);
--flxn-podium-2-label: #47666a;
--flxn-podium-3-bg: rgba(199, 110, 52, 0.12);
--flxn-podium-3-border: rgba(199, 110, 52, 0.42);
--flxn-podium-3-label: #8a4c1d;
}
/* ---------------------------------------------------------------- */
/* 3. Fixed backdrop (below all content) */
/* ---------------------------------------------------------------- */
[data-theme="ocean"] body::before {
content: "";
position: fixed;
inset: 0;
z-index: -1;
pointer-events: none;
}
/* Abyssal descent: deep teal fading to ink navy, sparse static bubbles
rising (bigger and brighter near the sea floor, fading toward the top). */
[data-theme="ocean"][data-mantine-color-scheme="dark"] body::before {
background-color: #062632;
background-image:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='640' viewBox='0 0 260 640'%3E%3Cg fill='none' stroke='%239beee2'%3E%3Ccircle cx='36' cy='584' r='5' stroke-opacity='.13'/%3E%3Ccircle cx='44' cy='512' r='3' stroke-opacity='.1'/%3E%3Ccircle cx='210' cy='612' r='4' stroke-opacity='.12'/%3E%3Ccircle cx='202' cy='540' r='2.5' stroke-opacity='.09'/%3E%3Ccircle cx='128' cy='430' r='3.5' stroke-opacity='.1'/%3E%3Ccircle cx='120' cy='360' r='2' stroke-opacity='.08'/%3E%3Ccircle cx='232' cy='300' r='3' stroke-opacity='.09'/%3E%3Ccircle cx='60' cy='250' r='2.5' stroke-opacity='.08'/%3E%3Ccircle cx='150' cy='160' r='2' stroke-opacity='.07'/%3E%3Ccircle cx='90' cy='80' r='1.5' stroke-opacity='.06'/%3E%3C/g%3E%3Cg fill='%239beee2'%3E%3Ccircle cx='70' cy='470' r='1.2' fill-opacity='.1'/%3E%3Ccircle cx='180' cy='390' r='1' fill-opacity='.09'/%3E%3Ccircle cx='40' cy='330' r='1' fill-opacity='.08'/%3E%3Ccircle cx='222' cy='190' r='1' fill-opacity='.07'/%3E%3Ccircle cx='140' cy='40' r='1' fill-opacity='.05'/%3E%3C/g%3E%3C/svg%3E"),
radial-gradient(50rem 30rem at 50% -10%, rgba(45, 160, 175, 0.18), transparent 65%),
linear-gradient(180deg, #0b3b46 0%, #072b38 45%, #03121e 100%);
background-repeat: repeat, no-repeat, no-repeat;
background-size: 260px 640px, auto, auto;
}
/* Sunlit shallows: seafoam fading to a sandy floor, with a faint white
caustic ripple tile — refracted light, kept very subtle. */
[data-theme="ocean"][data-mantine-color-scheme="light"] body::before {
background-color: #eaf6f6;
background-image:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='320' height='220' viewBox='0 0 320 220'%3E%3Cg fill='none' stroke='%23ffffff' stroke-width='1.4'%3E%3Cpath d='M0 40 Q 40 10 80 42 T 160 44 T 240 38 T 320 42' stroke-opacity='.4'/%3E%3Cpath d='M0 110 Q 45 82 90 112 T 180 110 T 268 114 T 320 108' stroke-opacity='.3'/%3E%3Cpath d='M0 180 Q 50 152 100 182 T 200 178 T 300 184 T 320 180' stroke-opacity='.35'/%3E%3C/g%3E%3C/svg%3E"),
radial-gradient(46rem 26rem at 30% -8%, rgba(255, 255, 255, 0.75), transparent 60%),
radial-gradient(60rem 22rem at 50% 108%, rgba(226, 214, 178, 0.5), transparent 70%),
linear-gradient(180deg, #d8efee 0%, #eaf6f6 55%, #f2f3e9 100%);
background-repeat: repeat, no-repeat, no-repeat, no-repeat;
background-size: 320px 220px, auto, auto, auto;
}
/* ---------------------------------------------------------------- */
/* 4. Pressure-hull surfaces: solid panels, bioluminescent rims */
/* ---------------------------------------------------------------- */
[data-theme="ocean"] .mantine-Paper-root,
[data-theme="ocean"] .mantine-Card-root {
background-color: var(--ocean-hull);
border: 1px solid var(--ocean-rim);
box-shadow: var(--mantine-shadow-sm);
}
[data-theme="ocean"] .mantine-Modal-content {
background-color: var(--ocean-hull);
border: 1px solid var(--ocean-rim-strong);
box-shadow: var(--mantine-shadow-xl);
}
[data-theme="ocean"] .mantine-Modal-header {
background-color: transparent;
}
[data-theme="ocean"] .mantine-Popover-dropdown {
background-color: var(--ocean-hull);
border: 1px solid var(--ocean-rim-strong);
box-shadow: var(--mantine-shadow-lg);
}
[data-theme="ocean"] .mantine-Notification-root {
background-color: var(--ocean-hull);
border: 1px solid var(--ocean-rim-strong);
box-shadow: var(--mantine-shadow-lg);
}
/* AppShell chrome: the conning tower and lower deck — solid hull strips
sealed with a stronger rim seam. */
[data-theme="ocean"] .mantine-AppShell-header {
background-color: var(--ocean-chrome);
border-bottom: 1px solid var(--ocean-rim-strong);
box-shadow: var(--mantine-shadow-sm);
}
[data-theme="ocean"] .mantine-AppShell-navbar {
background-color: var(--ocean-chrome);
border-inline-end: 1px solid var(--ocean-rim-strong);
}
/* ---------------------------------------------------------------- */
/* 5. Sonar focus rings */
/* ---------------------------------------------------------------- */
/* The transparent outline stays invisible here but becomes a real ring in
forced-colors mode, where the box-shadow sonar is stripped. */
[data-theme="ocean"] :focus-visible {
outline: 2px solid transparent;
outline-offset: 2px;
box-shadow: var(--ocean-sonar);
}
[data-theme="ocean"] .mantine-Input-input:focus,
[data-theme="ocean"] .mantine-Input-input:focus-within {
border-color: var(--mantine-primary-color-filled);
box-shadow: var(--ocean-sonar);
}
/* ---------------------------------------------------------------- */
/* 6. Controls */
/* ---------------------------------------------------------------- */
/* Filled controls give off a faint accent glow; the transparent border
keeps a real edge for forced-colors mode. */
[data-theme="ocean"] .mantine-Button-root[data-variant="filled"],
[data-theme="ocean"] .mantine-ActionIcon-root[data-variant="filled"] {
border: 1px solid transparent;
box-shadow:
0 0 14px -4px color-mix(in srgb, var(--mantine-primary-color-filled) 55%, transparent),
var(--mantine-shadow-xs);
}
[data-theme="ocean"] .mantine-Button-root {
font-weight: 600;
}
[data-theme="ocean"] .mantine-Input-input {
background-color: var(--ocean-deep);
border: 1px solid var(--ocean-rim);
}
[data-theme="ocean"] .mantine-Checkbox-input {
background-color: var(--ocean-deep);
border-color: var(--ocean-rim-strong);
}
[data-theme="ocean"] .mantine-SegmentedControl-root {
background-color: var(--ocean-deep);
border: 1px solid var(--ocean-rim);
}
[data-theme="ocean"] .mantine-SegmentedControl-indicator {
background-color: var(--ocean-hull-raised);
border: 1px solid var(--ocean-rim-strong);
box-shadow: var(--mantine-shadow-xs);
}
[data-theme="ocean"] .mantine-Progress-root {
background-color: var(--ocean-deep);
border: 1px solid var(--ocean-rim);
}
[data-theme="ocean"] .mantine-Badge-root[data-variant="default"] {
background-color: var(--mantine-color-default);
border: 1px solid var(--ocean-rim);
}
[data-theme="ocean"] .mantine-Tabs-list {
--tabs-list-border-color: var(--mantine-color-default-border);
}
[data-theme="ocean"] .mantine-Tabs-tab:hover,
[data-theme="ocean"] .mantine-NavLink-root:hover {
background-color: var(--mantine-color-default-hover);
}
[data-theme="ocean"] .mantine-Tooltip-tooltip {
background-color: var(--ocean-tooltip-bg);
color: var(--ocean-tooltip-color);
border-radius: var(--mantine-radius-md);
box-shadow: var(--mantine-shadow-sm);
}
[data-theme="ocean"] ::selection {
background: color-mix(in srgb, var(--mantine-primary-color-filled) 30%, transparent);
}
/* ---------------------------------------------------------------- */
/* 7. Wave divider: repeating radial scallop line */
/* ---------------------------------------------------------------- */
[data-theme="ocean"] .mantine-Divider-root {
--divider-color: var(--ocean-rim-strong);
}
[data-theme="ocean"] .mantine-Divider-root[data-orientation="horizontal"]:not([data-with-label]) {
border-top: none;
height: 5px;
background-image: radial-gradient(
circle at 6px -2px,
transparent 4px,
var(--ocean-rim-strong) 4px,
var(--ocean-rim-strong) 5.4px,
transparent 5.4px
);
background-size: 12px 5px;
background-repeat: repeat-x;
}
@media (forced-colors: active) {
[data-theme="ocean"] .mantine-Divider-root[data-orientation="horizontal"]:not([data-with-label]) {
border-top: 1px solid CanvasText;
height: auto;
background-image: none;
}
}
/* ---------------------------------------------------------------- */
/* 8. App component hooks */
/* ---------------------------------------------------------------- */
/* Tab strip: flat while resting; docked it becomes a hull deck strip. */
[data-theme="ocean"] .flxn-tabs[data-stuck] {
background-color: var(--ocean-chrome);
border-bottom-color: var(--ocean-rim-strong);
box-shadow: var(--mantine-shadow-sm);
}
[data-theme="ocean"] .flxn-tab {
color: var(--mantine-color-dimmed);
font-weight: 500;
}
/* Underline indicator, so accent text on the active tab is allowed. */
[data-theme="ocean"] .flxn-tab[data-active] {
color: var(--mantine-primary-color-light-color);
font-weight: 700;
}
[data-theme="ocean"] .flxn-tabs-indicator {
border-bottom: 2px solid var(--mantine-primary-color-filled);
padding-inline: 0.5rem;
}
/* Nav grid tiles: instrument panels — hull plate with a lit top seam. */
[data-theme="ocean"] .flxn-tile {
background-color: var(--ocean-hull);
border: 1px solid var(--ocean-rim);
border-radius: var(--mantine-radius-lg);
box-shadow: var(--mantine-shadow-xs), inset 0 1px 0 var(--ocean-rim);
}
/* Flat accent-tinted glyph — no plate. */
[data-theme="ocean"] .flxn-tile-icon {
color: var(--mantine-primary-color-light-color);
}
/* "See all" as a depth-tag chip: accent wash + its dedicated on-color. */
[data-theme="ocean"] .flxn-see-all {
background-color: var(--mantine-primary-color-light);
color: var(--mantine-primary-color-light-color);
border: 1px solid
color-mix(in srgb, var(--mantine-primary-color-light-color) 35%, transparent);
}
/* Match card + reaction tray as one hull construction: same rim, tray one
deck lower (deeper water). */
[data-theme="ocean"] .flxn-match-card {
background-color: var(--ocean-hull);
border: 1px solid var(--ocean-rim);
}
[data-theme="ocean"] .flxn-reaction-row {
background-color: var(--ocean-deep);
border: 1px solid var(--ocean-rim);
}
/* Porthole: the bold ring comes from --flxn-avatar-border; the frame adds
the bioluminescent halo around it. */
[data-theme="ocean"] .flxn-avatar-frame {
border-radius: 50%;
box-shadow: var(--ocean-porthole-glow);
}
/* Press feedback: a brief bioluminescent flare (filter only — the global
scale transform stays untouched). */
[data-theme="ocean"] .flxn-press:active {
filter: brightness(1.06) saturate(1.08);
}
+18
View File
@@ -0,0 +1,18 @@
import { msg } from "@lingui/core/macro";
import type { ThemeManifest } from "../types";
const manifest: ThemeManifest = {
id: "quest",
label: msg`Quest Log`,
colorSchemes: "both",
fontLinks: [
"https://fonts.googleapis.com/css2?family=Grenze:wght@500;600;700&display=swap",
"https://fonts.googleapis.com/css2?family=Alegreya:ital,wght@0,400;0,500;0,600;0,700;1,400;1,600&display=swap",
],
chromeColors: {
light: { base: "#f0e6cf", dimmed: "#ddcca6" },
dark: { base: "#211a12", dimmed: "#150f08" },
},
};
export default manifest;
+762
View File
@@ -0,0 +1,762 @@
/* Quest Log — fantasy-RPG journal.
Aged bronze is the structural decoration (2px entry frames, corner
brackets, ribbon banners); the interactive accent follows the user via
the primary aliases. All eight accent ramps are retuned as heraldic
banner dyes. Light = aged parchment journal, dark = torchlit hall. */
/* ---------------------------------------------------------------- *
* 1. Scheme-independent tokens — (0,2,0)
* ---------------------------------------------------------------- */
[data-theme="quest"][data-theme] {
/* Type: Grenze for medieval-but-readable headings, Alegreya body. */
--mantine-font-family: "Alegreya", "Iowan Old Style", Georgia, serif;
--mantine-font-family-headings: "Grenze", "Alegreya", Georgia, serif;
--mantine-font-size-xs: 0.8125rem;
--mantine-font-size-sm: 0.9375rem;
--mantine-font-size-md: 1.0625rem;
--mantine-font-size-lg: 1.1875rem;
--mantine-font-size-xl: 1.375rem;
--mantine-heading-font-weight: 600;
--mantine-h1-font-size: 2.4rem;
--mantine-h1-line-height: 1.15;
--mantine-h2-font-size: 1.95rem;
--mantine-h2-line-height: 1.2;
--mantine-h3-font-size: 1.55rem;
--mantine-h3-line-height: 1.25;
--mantine-h4-font-size: 1.3rem;
--mantine-h4-line-height: 1.3;
--mantine-h5-font-size: 1.15rem;
--mantine-h5-line-height: 1.35;
--mantine-h6-font-size: 1.05rem;
--mantine-h6-line-height: 1.4;
/* Shape: gently squared journal entries. */
--mantine-radius-xs: 0.125rem;
--mantine-radius-sm: 0.25rem;
--mantine-radius-md: 0.375rem;
--mantine-radius-lg: 0.5rem;
--mantine-radius-xl: 0.75rem;
--mantine-radius-default: 0.375rem;
/* Elevation: soft warm sepia, never harsh. */
--mantine-shadow-xs: 0 1px 2px rgba(38, 26, 10, 0.2);
--mantine-shadow-sm: 0 1px 2px rgba(38, 26, 10, 0.22), 0 5px 14px rgba(38, 26, 10, 0.16);
--mantine-shadow-md: 0 2px 4px rgba(38, 26, 10, 0.24), 0 10px 24px rgba(38, 26, 10, 0.2);
--mantine-shadow-lg: 0 3px 7px rgba(38, 26, 10, 0.26), 0 18px 40px rgba(38, 26, 10, 0.24);
--mantine-shadow-xl: 0 5px 10px rgba(38, 26, 10, 0.28), 0 26px 56px rgba(38, 26, 10, 0.28);
/* Accent ramps retuned as heraldic banner dyes — saturated but cloth-worn;
every shade 6 carries ≥4.5:1 parchment contrast text. */
/* blue → azure banner */
--mantine-color-blue-0: #eef1fa;
--mantine-color-blue-1: #d8dff2;
--mantine-color-blue-2: #b4c2e6;
--mantine-color-blue-3: #8aa0d6;
--mantine-color-blue-4: #6280c4;
--mantine-color-blue-5: #4263ae;
--mantine-color-blue-6: #2f4d96;
--mantine-color-blue-7: #263e7b;
--mantine-color-blue-8: #1d2f60;
--mantine-color-blue-9: #152246;
/* red → gules crimson */
--mantine-color-red-0: #faeceb;
--mantine-color-red-1: #f2d3d1;
--mantine-color-red-2: #e6aba7;
--mantine-color-red-3: #d87d78;
--mantine-color-red-4: #c65450;
--mantine-color-red-5: #ad322f;
--mantine-color-red-6: #92201f;
--mantine-color-red-7: #7a1719;
--mantine-color-red-8: #601114;
--mantine-color-red-9: #470c0f;
/* green → vert */
--mantine-color-green-0: #ebf3ec;
--mantine-color-green-1: #d2e5d4;
--mantine-color-green-2: #aacfae;
--mantine-color-green-3: #7db383;
--mantine-color-green-4: #55975d;
--mantine-color-green-5: #35793f;
--mantine-color-green-6: #24632e;
--mantine-color-green-7: #1c5026;
--mantine-color-green-8: #153e1e;
--mantine-color-green-9: #0e2c16;
/* yellow → or (gold leaf) */
--mantine-color-yellow-0: #fbf3dc;
--mantine-color-yellow-1: #f4e3b0;
--mantine-color-yellow-2: #e9cd7c;
--mantine-color-yellow-3: #dbb14b;
--mantine-color-yellow-4: #c99527;
--mantine-color-yellow-5: #ab7812;
--mantine-color-yellow-6: #8c5f0b;
--mantine-color-yellow-7: #714c09;
--mantine-color-yellow-8: #583b07;
--mantine-color-yellow-9: #402b05;
/* grape → purpure */
--mantine-color-grape-0: #f4edf7;
--mantine-color-grape-1: #e5d5ec;
--mantine-color-grape-2: #cfb2dc;
--mantine-color-grape-3: #b389c8;
--mantine-color-grape-4: #9663b2;
--mantine-color-grape-5: #7a4399;
--mantine-color-grape-6: #642e82;
--mantine-color-grape-7: #52246b;
--mantine-color-grape-8: #401b54;
--mantine-color-grape-9: #2f133e;
/* orange → tenné */
--mantine-color-orange-0: #faeee2;
--mantine-color-orange-1: #f3d9bd;
--mantine-color-orange-2: #e9bc8d;
--mantine-color-orange-3: #dd9a58;
--mantine-color-orange-4: #cf7a2c;
--mantine-color-orange-5: #b45e14;
--mantine-color-orange-6: #96490d;
--mantine-color-orange-7: #7a3a0b;
--mantine-color-orange-8: #602d08;
--mantine-color-orange-9: #472106;
/* pink → rose madder */
--mantine-color-pink-0: #f9ecf1;
--mantine-color-pink-1: #f0d3de;
--mantine-color-pink-2: #e3aac1;
--mantine-color-pink-3: #d27da0;
--mantine-color-pink-4: #bf5280;
--mantine-color-pink-5: #a53064;
--mantine-color-pink-6: #8a1f51;
--mantine-color-pink-7: #711842;
--mantine-color-pink-8: #581233;
--mantine-color-pink-9: #400c25;
/* lime → spring leaf */
--mantine-color-lime-0: #f2f5e6;
--mantine-color-lime-1: #e2e9c6;
--mantine-color-lime-2: #cdd89e;
--mantine-color-lime-3: #b2c26f;
--mantine-color-lime-4: #96a944;
--mantine-color-lime-5: #7a8d26;
--mantine-color-lime-6: #59690f;
--mantine-color-lime-7: #485510;
--mantine-color-lime-8: #38420d;
--mantine-color-lime-9: #293009;
/* Gray ramp as parchment/sepia so gray-derived UI stays in the world. */
--mantine-color-gray-0: #f7efdd;
--mantine-color-gray-1: #f0e6cf;
--mantine-color-gray-2: #e3d4b4;
--mantine-color-gray-3: #d0bd95;
--mantine-color-gray-4: #b5a17a;
--mantine-color-gray-5: #92805e;
--mantine-color-gray-6: #6a5a41;
--mantine-color-gray-7: #4a3d2b;
--mantine-color-gray-8: #362c1e;
--mantine-color-gray-9: #291f14;
/* Dark ramp as torch-lit umber. */
--mantine-color-dark-0: #ecdfc6;
--mantine-color-dark-1: #b3a488;
--mantine-color-dark-2: #7a6a52;
--mantine-color-dark-3: #55462f;
--mantine-color-dark-4: #3b2f1f;
--mantine-color-dark-5: #2b2318;
--mantine-color-dark-6: #241d13;
--mantine-color-dark-7: #1c160e;
--mantine-color-dark-8: #15100a;
--mantine-color-dark-9: #0e0a06;
--flxn-avatar-radius: 50%; /* party-member portraits */
--flxn-avatar-border: 2px solid var(--quest-frame);
--flxn-badge-radius: 8px;
}
/* ---------------------------------------------------------------- *
* 2. Per-scheme tokens — (0,3,0)
* ---------------------------------------------------------------- */
[data-theme="quest"][data-theme][data-mantine-color-scheme="light"] {
--mantine-color-body: #f0e6cf;
--mantine-color-text: #2f2414;
--mantine-color-bright: #1d1509;
--mantine-color-dimmed: #6a5a41;
--mantine-color-placeholder: #83704f;
--mantine-color-anchor: var(--mantine-primary-color-7);
--mantine-color-error: #9e2b20;
--mantine-color-default: #f7efdd;
--mantine-color-default-hover: #efe3c8;
--mantine-color-default-color: #2f2414;
--mantine-color-default-border: #b39a6b;
/* Accent variant vars re-derived from the retuned ramps (Mantine's
defaults are literal rgba of its stock hues). */
--mantine-color-blue-light: color-mix(in srgb, var(--mantine-color-blue-6) 10%, transparent);
--mantine-color-blue-light-hover: color-mix(in srgb, var(--mantine-color-blue-6) 16%, transparent);
--mantine-color-blue-light-color: var(--mantine-color-blue-7);
--mantine-color-blue-outline: var(--mantine-color-blue-6);
--mantine-color-blue-outline-hover: color-mix(in srgb, var(--mantine-color-blue-6) 8%, transparent);
--mantine-color-blue-text: var(--mantine-color-blue-7);
--mantine-color-blue-contrast: #f6ecd8;
--mantine-color-red-light: color-mix(in srgb, var(--mantine-color-red-6) 10%, transparent);
--mantine-color-red-light-hover: color-mix(in srgb, var(--mantine-color-red-6) 16%, transparent);
--mantine-color-red-light-color: var(--mantine-color-red-7);
--mantine-color-red-outline: var(--mantine-color-red-6);
--mantine-color-red-outline-hover: color-mix(in srgb, var(--mantine-color-red-6) 8%, transparent);
--mantine-color-red-text: var(--mantine-color-red-7);
--mantine-color-red-contrast: #f6ecd8;
--mantine-color-green-light: color-mix(in srgb, var(--mantine-color-green-6) 10%, transparent);
--mantine-color-green-light-hover: color-mix(in srgb, var(--mantine-color-green-6) 16%, transparent);
--mantine-color-green-light-color: var(--mantine-color-green-7);
--mantine-color-green-outline: var(--mantine-color-green-6);
--mantine-color-green-outline-hover: color-mix(in srgb, var(--mantine-color-green-6) 8%, transparent);
--mantine-color-green-text: var(--mantine-color-green-7);
--mantine-color-green-contrast: #f6ecd8;
--mantine-color-yellow-light: color-mix(in srgb, var(--mantine-color-yellow-6) 10%, transparent);
--mantine-color-yellow-light-hover: color-mix(in srgb, var(--mantine-color-yellow-6) 16%, transparent);
--mantine-color-yellow-light-color: var(--mantine-color-yellow-7);
--mantine-color-yellow-outline: var(--mantine-color-yellow-6);
--mantine-color-yellow-outline-hover: color-mix(in srgb, var(--mantine-color-yellow-6) 8%, transparent);
--mantine-color-yellow-text: var(--mantine-color-yellow-7);
--mantine-color-yellow-contrast: #f6ecd8;
--mantine-color-grape-light: color-mix(in srgb, var(--mantine-color-grape-6) 10%, transparent);
--mantine-color-grape-light-hover: color-mix(in srgb, var(--mantine-color-grape-6) 16%, transparent);
--mantine-color-grape-light-color: var(--mantine-color-grape-7);
--mantine-color-grape-outline: var(--mantine-color-grape-6);
--mantine-color-grape-outline-hover: color-mix(in srgb, var(--mantine-color-grape-6) 8%, transparent);
--mantine-color-grape-text: var(--mantine-color-grape-7);
--mantine-color-grape-contrast: #f6ecd8;
--mantine-color-orange-light: color-mix(in srgb, var(--mantine-color-orange-6) 10%, transparent);
--mantine-color-orange-light-hover: color-mix(in srgb, var(--mantine-color-orange-6) 16%, transparent);
--mantine-color-orange-light-color: var(--mantine-color-orange-7);
--mantine-color-orange-outline: var(--mantine-color-orange-6);
--mantine-color-orange-outline-hover: color-mix(in srgb, var(--mantine-color-orange-6) 8%, transparent);
--mantine-color-orange-text: var(--mantine-color-orange-7);
--mantine-color-orange-contrast: #f6ecd8;
--mantine-color-pink-light: color-mix(in srgb, var(--mantine-color-pink-6) 10%, transparent);
--mantine-color-pink-light-hover: color-mix(in srgb, var(--mantine-color-pink-6) 16%, transparent);
--mantine-color-pink-light-color: var(--mantine-color-pink-7);
--mantine-color-pink-outline: var(--mantine-color-pink-6);
--mantine-color-pink-outline-hover: color-mix(in srgb, var(--mantine-color-pink-6) 8%, transparent);
--mantine-color-pink-text: var(--mantine-color-pink-7);
--mantine-color-pink-contrast: #f6ecd8;
--mantine-color-lime-light: color-mix(in srgb, var(--mantine-color-lime-6) 10%, transparent);
--mantine-color-lime-light-hover: color-mix(in srgb, var(--mantine-color-lime-6) 16%, transparent);
--mantine-color-lime-light-color: var(--mantine-color-lime-7);
--mantine-color-lime-outline: var(--mantine-color-lime-6);
--mantine-color-lime-outline-hover: color-mix(in srgb, var(--mantine-color-lime-6) 8%, transparent);
--mantine-color-lime-text: var(--mantine-color-lime-7);
--mantine-color-lime-contrast: #f6ecd8;
--mantine-color-gray-light: color-mix(in srgb, var(--mantine-color-gray-6) 10%, transparent);
--mantine-color-gray-light-hover: color-mix(in srgb, var(--mantine-color-gray-6) 15%, transparent);
--mantine-color-gray-light-color: var(--mantine-color-gray-7);
/* Theme-local palette. */
--quest-frame: #a3854f;
--quest-corner: #8a6a3a;
--quest-bronze: #7c5f2c;
--quest-surface: #f7efdd;
--quest-recessed: #e7d8b6;
--quest-shell: #ece0c3;
--quest-tick: rgba(90, 66, 30, 0.22);
--quest-ribbon-hi: #8d6c38;
--quest-ribbon: #7a5c2e;
--quest-ribbon-lo: #5f4620;
--quest-ribbon-ink: #f7eedb;
/* App tokens: guild-board accolades on parchment. */
--flxn-gold: #7d5c12;
--flxn-silver: #776e5c;
--flxn-bronze: #8a4f22;
--flxn-toast-success: #45702c;
--flxn-toast-error: #9e2b20;
--flxn-podium-1-bg: color-mix(in srgb, #c99527 15%, #f7efdd);
--flxn-podium-1-border: color-mix(in srgb, #7d5c12 55%, #d0bd95);
--flxn-podium-1-label: #6f5116;
--flxn-podium-2-bg: color-mix(in srgb, #92805e 10%, #f7efdd);
--flxn-podium-2-border: #b5a17a;
--flxn-podium-2-label: #6a5a41;
--flxn-podium-3-bg: color-mix(in srgb, #8a4f22 12%, #f7efdd);
--flxn-podium-3-border: color-mix(in srgb, #8a4f22 45%, #d0bd95);
--flxn-podium-3-label: #74431c;
--flxn-showcase-bg: #f7efdd;
--flxn-overlay: rgba(35, 24, 10, 0.45);
--flxn-dotgrid-color: rgba(138, 106, 58, 0.25);
--flxn-pop-shadow: rgba(46, 32, 12, 0.3);
}
[data-theme="quest"][data-theme][data-mantine-color-scheme="dark"] {
--mantine-color-body: #211a12;
--mantine-color-text: #ecdfc6;
--mantine-color-bright: #f7efdd;
--mantine-color-dimmed: #a5937a;
--mantine-color-placeholder: #837259;
--mantine-color-anchor: var(--mantine-primary-color-2);
--mantine-color-error: #d4736a;
--mantine-color-default: #2b2216;
--mantine-color-default-hover: #342a1b;
--mantine-color-default-color: #ecdfc6;
--mantine-color-default-border: #5c4a2c;
/* Filled stays mid-ramp: shade-8 dye vanishes in the torchlit hall. */
--mantine-color-blue-filled: var(--mantine-color-blue-6);
--mantine-color-blue-filled-hover: var(--mantine-color-blue-5);
--mantine-color-blue-light: color-mix(in srgb, var(--mantine-color-blue-4) 15%, transparent);
--mantine-color-blue-light-hover: color-mix(in srgb, var(--mantine-color-blue-4) 22%, transparent);
--mantine-color-blue-light-color: var(--mantine-color-blue-2);
--mantine-color-blue-outline: var(--mantine-color-blue-3);
--mantine-color-blue-outline-hover: color-mix(in srgb, var(--mantine-color-blue-3) 12%, transparent);
--mantine-color-blue-text: var(--mantine-color-blue-3);
--mantine-color-blue-contrast: #f6ecd8;
--mantine-color-red-filled: var(--mantine-color-red-6);
--mantine-color-red-filled-hover: var(--mantine-color-red-5);
--mantine-color-red-light: color-mix(in srgb, var(--mantine-color-red-4) 15%, transparent);
--mantine-color-red-light-hover: color-mix(in srgb, var(--mantine-color-red-4) 22%, transparent);
--mantine-color-red-light-color: var(--mantine-color-red-2);
--mantine-color-red-outline: var(--mantine-color-red-3);
--mantine-color-red-outline-hover: color-mix(in srgb, var(--mantine-color-red-3) 12%, transparent);
--mantine-color-red-text: var(--mantine-color-red-3);
--mantine-color-red-contrast: #f6ecd8;
--mantine-color-green-filled: var(--mantine-color-green-6);
--mantine-color-green-filled-hover: var(--mantine-color-green-5);
--mantine-color-green-light: color-mix(in srgb, var(--mantine-color-green-4) 15%, transparent);
--mantine-color-green-light-hover: color-mix(in srgb, var(--mantine-color-green-4) 22%, transparent);
--mantine-color-green-light-color: var(--mantine-color-green-2);
--mantine-color-green-outline: var(--mantine-color-green-3);
--mantine-color-green-outline-hover: color-mix(in srgb, var(--mantine-color-green-3) 12%, transparent);
--mantine-color-green-text: var(--mantine-color-green-3);
--mantine-color-green-contrast: #f6ecd8;
--mantine-color-yellow-filled: var(--mantine-color-yellow-6);
--mantine-color-yellow-filled-hover: var(--mantine-color-yellow-5);
--mantine-color-yellow-light: color-mix(in srgb, var(--mantine-color-yellow-4) 15%, transparent);
--mantine-color-yellow-light-hover: color-mix(in srgb, var(--mantine-color-yellow-4) 22%, transparent);
--mantine-color-yellow-light-color: var(--mantine-color-yellow-2);
--mantine-color-yellow-outline: var(--mantine-color-yellow-3);
--mantine-color-yellow-outline-hover: color-mix(in srgb, var(--mantine-color-yellow-3) 12%, transparent);
--mantine-color-yellow-text: var(--mantine-color-yellow-3);
--mantine-color-yellow-contrast: #f6ecd8;
--mantine-color-grape-filled: var(--mantine-color-grape-6);
--mantine-color-grape-filled-hover: var(--mantine-color-grape-5);
--mantine-color-grape-light: color-mix(in srgb, var(--mantine-color-grape-4) 15%, transparent);
--mantine-color-grape-light-hover: color-mix(in srgb, var(--mantine-color-grape-4) 22%, transparent);
--mantine-color-grape-light-color: var(--mantine-color-grape-2);
--mantine-color-grape-outline: var(--mantine-color-grape-3);
--mantine-color-grape-outline-hover: color-mix(in srgb, var(--mantine-color-grape-3) 12%, transparent);
--mantine-color-grape-text: var(--mantine-color-grape-3);
--mantine-color-grape-contrast: #f6ecd8;
--mantine-color-orange-filled: var(--mantine-color-orange-6);
--mantine-color-orange-filled-hover: var(--mantine-color-orange-5);
--mantine-color-orange-light: color-mix(in srgb, var(--mantine-color-orange-4) 15%, transparent);
--mantine-color-orange-light-hover: color-mix(in srgb, var(--mantine-color-orange-4) 22%, transparent);
--mantine-color-orange-light-color: var(--mantine-color-orange-2);
--mantine-color-orange-outline: var(--mantine-color-orange-3);
--mantine-color-orange-outline-hover: color-mix(in srgb, var(--mantine-color-orange-3) 12%, transparent);
--mantine-color-orange-text: var(--mantine-color-orange-3);
--mantine-color-orange-contrast: #f6ecd8;
--mantine-color-pink-filled: var(--mantine-color-pink-6);
--mantine-color-pink-filled-hover: var(--mantine-color-pink-5);
--mantine-color-pink-light: color-mix(in srgb, var(--mantine-color-pink-4) 15%, transparent);
--mantine-color-pink-light-hover: color-mix(in srgb, var(--mantine-color-pink-4) 22%, transparent);
--mantine-color-pink-light-color: var(--mantine-color-pink-2);
--mantine-color-pink-outline: var(--mantine-color-pink-3);
--mantine-color-pink-outline-hover: color-mix(in srgb, var(--mantine-color-pink-3) 12%, transparent);
--mantine-color-pink-text: var(--mantine-color-pink-3);
--mantine-color-pink-contrast: #f6ecd8;
--mantine-color-lime-filled: var(--mantine-color-lime-6);
--mantine-color-lime-filled-hover: var(--mantine-color-lime-5);
--mantine-color-lime-light: color-mix(in srgb, var(--mantine-color-lime-4) 15%, transparent);
--mantine-color-lime-light-hover: color-mix(in srgb, var(--mantine-color-lime-4) 22%, transparent);
--mantine-color-lime-light-color: var(--mantine-color-lime-2);
--mantine-color-lime-outline: var(--mantine-color-lime-3);
--mantine-color-lime-outline-hover: color-mix(in srgb, var(--mantine-color-lime-3) 12%, transparent);
--mantine-color-lime-text: var(--mantine-color-lime-3);
--mantine-color-lime-contrast: #f6ecd8;
--mantine-color-gray-light: color-mix(in srgb, var(--mantine-color-gray-5) 12%, transparent);
--mantine-color-gray-light-hover: color-mix(in srgb, var(--mantine-color-gray-5) 18%, transparent);
--mantine-color-gray-light-color: var(--mantine-color-gray-2);
/* Theme-local palette: ember-warm parchment panels in a torchlit hall. */
--quest-frame: #7a6136;
--quest-corner: #c49a5e;
--quest-bronze: #cfa768;
--quest-surface: #2b2216;
--quest-recessed: #171208;
--quest-shell: #1a140c;
--quest-tick: rgba(0, 0, 0, 0.4);
--quest-ribbon-hi: #d8b077;
--quest-ribbon: #c49a5e;
--quest-ribbon-lo: #a97f45;
--quest-ribbon-ink: #241a0c;
--flxn-gold: #d9b36b;
--flxn-silver: #a89f90;
--flxn-bronze: #c08050;
--flxn-toast-success: #a8bd85;
--flxn-toast-error: #dc9a90;
--flxn-podium-1-bg: color-mix(in srgb, #c99527 14%, #2b2216);
--flxn-podium-1-border: color-mix(in srgb, #d9b36b 45%, #3b2f1f);
--flxn-podium-1-label: #d9b36b;
--flxn-podium-2-bg: color-mix(in srgb, #a89f90 9%, #2b2216);
--flxn-podium-2-border: color-mix(in srgb, #a89f90 32%, #3b2f1f);
--flxn-podium-2-label: #b5aea1;
--flxn-podium-3-bg: color-mix(in srgb, #c08050 12%, #2b2216);
--flxn-podium-3-border: color-mix(in srgb, #c08050 42%, #3b2f1f);
--flxn-podium-3-label: #cd8f61;
--flxn-showcase-bg: #2b2216;
--flxn-overlay: rgba(8, 5, 2, 0.6);
--flxn-dotgrid-color: rgba(196, 154, 94, 0.18);
--flxn-pop-shadow: rgba(0, 0, 0, 0.55);
}
/* ---------------------------------------------------------------- *
* 3. Full-page decoration: worn parchment edges / torchlight
* ---------------------------------------------------------------- */
[data-theme="quest"][data-mantine-color-scheme="light"] body::before {
content: "";
position: fixed;
inset: 0;
z-index: 9998;
pointer-events: none;
background:
radial-gradient(140% 100% at 50% 32%, transparent 58%, rgba(122, 91, 42, 0.1) 88%, rgba(96, 69, 28, 0.2) 100%),
radial-gradient(55% 38% at 6% 2%, rgba(122, 91, 42, 0.07), transparent 70%),
radial-gradient(55% 38% at 95% 98%, rgba(122, 91, 42, 0.07), transparent 70%);
}
[data-theme="quest"][data-mantine-color-scheme="dark"] body::before {
content: "";
position: fixed;
inset: 0;
z-index: 9998;
pointer-events: none;
background:
radial-gradient(90% 55% at 50% 0%, rgba(214, 158, 84, 0.07), transparent 70%),
radial-gradient(130% 95% at 50% 45%, transparent 55%, rgba(5, 3, 1, 0.5) 100%);
}
/* ---------------------------------------------------------------- *
* 4. Quest entries: 2px bronze frames + ornate corner brackets
* ---------------------------------------------------------------- */
[data-theme="quest"] .mantine-Paper-root[data-with-border],
[data-theme="quest"] .mantine-Card-root,
[data-theme="quest"] .mantine-Modal-content,
[data-theme="quest"] .flxn-tile,
[data-theme="quest"] .flxn-match-card {
position: relative;
background-color: var(--quest-surface);
border: 2px solid var(--quest-frame);
}
/* One pseudo paints all four L-shaped brackets (2 strips per corner). */
[data-theme="quest"] .mantine-Paper-root[data-with-border]::before,
[data-theme="quest"] .mantine-Card-root::before,
[data-theme="quest"] .mantine-Modal-content::before,
[data-theme="quest"] .flxn-tile::before,
[data-theme="quest"] .flxn-match-card::before {
content: "";
position: absolute;
inset: 3px;
pointer-events: none;
background-image:
linear-gradient(var(--quest-corner), var(--quest-corner)),
linear-gradient(var(--quest-corner), var(--quest-corner)),
linear-gradient(var(--quest-corner), var(--quest-corner)),
linear-gradient(var(--quest-corner), var(--quest-corner)),
linear-gradient(var(--quest-corner), var(--quest-corner)),
linear-gradient(var(--quest-corner), var(--quest-corner)),
linear-gradient(var(--quest-corner), var(--quest-corner)),
linear-gradient(var(--quest-corner), var(--quest-corner));
background-repeat: no-repeat;
background-size:
12px 2px, 2px 12px,
12px 2px, 2px 12px,
12px 2px, 2px 12px,
12px 2px, 2px 12px;
background-position:
left top, left top,
right top, right top,
left bottom, left bottom,
right bottom, right bottom;
}
/* ---------------------------------------------------------------- *
* 5. Component restyles
* ---------------------------------------------------------------- */
/* Headings: Grenze; section headers (h3/h4) ride ribbon banners. */
[data-theme="quest"] .mantine-Title-root {
letter-spacing: 0.01em;
}
[data-theme="quest"] h3.mantine-Title-root,
[data-theme="quest"] h4.mantine-Title-root {
width: fit-content;
max-width: 100%;
padding: 0.12em 1.1em 0.18em;
background-image: linear-gradient(
180deg,
var(--quest-ribbon-hi) 0%,
var(--quest-ribbon) 55%,
var(--quest-ribbon-lo) 100%
);
color: var(--quest-ribbon-ink);
clip-path: polygon(
0 0,
100% 0,
calc(100% - 0.55em) 50%,
100% 100%,
0 100%,
0.55em 50%
);
}
/* Buttons: banner-dye solids in the user's accent; bronze-ruled defaults. */
[data-theme="quest"] .mantine-Button-root {
font-weight: 600;
letter-spacing: 0.02em;
}
[data-theme="quest"] .mantine-Button-root[data-variant="filled"],
[data-theme="quest"] .mantine-ActionIcon-root[data-variant="filled"] {
border: 1px solid color-mix(in srgb, var(--mantine-primary-color-filled) 55%, black);
box-shadow: inset 0 1px 0 rgba(255, 250, 235, 0.2), 0 1px 3px var(--flxn-pop-shadow);
}
[data-theme="quest"] .mantine-Button-root[data-variant="default"],
[data-theme="quest"] .mantine-ActionIcon-root[data-variant="default"] {
border-color: var(--quest-frame);
background-color: var(--quest-surface);
}
/* Inputs: recessed ledger lines; focus follows the accent. */
[data-theme="quest"] .mantine-Input-input {
background-color: var(--quest-recessed);
border-color: var(--quest-frame);
}
[data-theme="quest"] .mantine-PinInput-input {
background-color: var(--quest-recessed);
}
/* Mantine Tabs: illuminated active label under a bronze rule. */
[data-theme="quest"] .mantine-Tabs-tab {
letter-spacing: 0.02em;
}
[data-theme="quest"] .mantine-Tabs-tab[data-active] {
border-color: var(--quest-corner);
color: var(--mantine-primary-color-light-color);
}
/* Nav: active entries follow the accent — wash plus a marginal rule. */
[data-theme="quest"] .mantine-NavLink-root[data-active] {
background-color: var(--mantine-primary-color-light);
color: var(--mantine-primary-color-light-color);
box-shadow: inset 3px 0 0 var(--mantine-primary-color-filled);
}
[data-theme="quest"] .mantine-NavLink-root[data-active]:hover {
background-color: var(--mantine-primary-color-light-hover);
}
/* Shell: the journal's leather binding. */
[data-theme="quest"] .mantine-AppShell-header {
background-color: var(--quest-shell);
border-bottom: 1px solid var(--quest-frame);
}
[data-theme="quest"] .mantine-AppShell-navbar {
background-color: var(--quest-shell);
border-color: var(--quest-frame);
}
/* Segmented control: carved channel with a parchment slider. */
[data-theme="quest"] .mantine-SegmentedControl-root {
background-color: var(--quest-recessed);
border: 1px solid var(--quest-frame);
}
[data-theme="quest"] .mantine-SegmentedControl-indicator {
background-color: var(--quest-surface);
border: 1px solid var(--quest-frame);
box-shadow: 0 1px 3px var(--flxn-pop-shadow);
}
/* Progress: XP bars — framed groove, segment ticks, glossed accent fill. */
[data-theme="quest"] .mantine-Progress-root {
position: relative;
background-color: var(--quest-recessed);
border: 1px solid var(--quest-frame);
box-shadow: inset 0 1px 2px rgba(20, 12, 4, 0.3);
}
[data-theme="quest"] .mantine-Progress-root::after {
content: "";
position: absolute;
inset: 0;
z-index: 1;
pointer-events: none;
background-image: repeating-linear-gradient(
90deg,
transparent 0,
transparent 11px,
var(--quest-tick) 11px,
var(--quest-tick) 12px
);
}
[data-theme="quest"] .mantine-Progress-section {
background-image: linear-gradient(
180deg,
rgba(255, 246, 224, 0.32) 0%,
rgba(255, 246, 224, 0.06) 45%,
rgba(24, 14, 4, 0.14) 100%
);
}
/* Badges: embossed wax/metal seals; filled keeps a legible on-color. */
[data-theme="quest"] .mantine-Badge-root {
font-weight: 700;
letter-spacing: 0.05em;
border: 1px solid rgba(24, 14, 4, 0.25);
box-shadow: inset 0 1px 0 rgba(255, 250, 235, 0.28),
inset 0 -1px 0 rgba(24, 14, 4, 0.18), 0 1px 2px var(--flxn-pop-shadow);
}
[data-theme="quest"] .mantine-Badge-root[data-variant="filled"],
[data-theme="quest"] .mantine-Badge-root:not([data-variant]),
[data-theme="quest"] .mantine-ThemeIcon-root[data-variant="filled"],
[data-theme="quest"] .mantine-ThemeIcon-root:not([data-variant]) {
color: var(--mantine-primary-color-contrast);
}
[data-theme="quest"] .mantine-Checkbox-root {
--checkbox-icon-color: var(--mantine-primary-color-contrast);
}
/* Dividers: bronze rule with a compass-star waypoint. */
[data-theme="quest"] .mantine-Divider-root {
--divider-color: color-mix(in srgb, var(--quest-frame) 60%, transparent);
}
[data-theme="quest"] .mantine-Divider-root[data-orientation="horizontal"]:not([data-with-label]) {
position: relative;
overflow: visible;
}
[data-theme="quest"] .mantine-Divider-root[data-orientation="horizontal"]:not([data-with-label])::before {
content: "\2726"; /* ✦ */
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding: 0 0.5em;
background-color: var(--mantine-color-body);
color: var(--quest-bronze);
font-size: 0.7rem;
line-height: 1;
}
[data-theme="quest"] .mantine-Divider-label {
color: var(--quest-bronze);
letter-spacing: 0.08em;
text-transform: uppercase;
font-size: var(--mantine-font-size-xs);
}
/* Overlays: sealed notes. Popovers get a single bronze rule (the corner
brackets are reserved for full entries; Modal-content has them above). */
[data-theme="quest"] .mantine-Popover-dropdown {
background-color: var(--quest-surface);
border: 1px solid var(--quest-frame);
}
[data-theme="quest"] .mantine-Modal-header {
background-color: var(--quest-surface);
}
[data-theme="quest"] .mantine-Tooltip-tooltip {
background-color: #1c160e;
color: #ecdfc6;
border: 1px solid var(--quest-frame);
}
/* Notifications: dispatched scrolls. */
[data-theme="quest"] .mantine-Notification-root {
background-color: var(--quest-surface);
border: 1px solid var(--quest-frame);
box-shadow: var(--mantine-shadow-md);
}
/* Alerts keep a real border for forced-colors resilience. */
[data-theme="quest"] .mantine-Alert-root {
border: 1px solid var(--quest-frame);
}
[data-theme="quest"][data-mantine-color-scheme="light"] .mantine-Skeleton-root::after {
background-color: #e3d3ae;
}
/* ---------------------------------------------------------------- *
* 6. App component hooks
* ---------------------------------------------------------------- */
/* Tab strip: flat chapter tabs resting; docked = binding strip with a
bronze rule. Underline indicator, so accent text on the tab is fine. */
[data-theme="quest"] .flxn-tabs[data-stuck] {
background-color: var(--quest-shell);
border-bottom-color: var(--quest-frame);
}
[data-theme="quest"] .flxn-tab {
color: var(--mantine-color-dimmed);
letter-spacing: 0.02em;
}
[data-theme="quest"] .flxn-tab[data-active] {
color: var(--mantine-primary-color-light-color);
font-weight: 700;
}
[data-theme="quest"] .flxn-tabs-indicator {
border-bottom: 3px solid var(--mantine-primary-color-filled);
padding-inline: 0.5rem;
}
/* Nav grid tiles: quest entries (frame + brackets set in section 4);
glyphs stay FLAT — bronze-inked tint only. */
[data-theme="quest"] .flxn-tile-icon {
color: var(--quest-bronze);
}
/* "See all": wax-seal-tabbed chip — parchment chip, accent seal dot,
accent ink on a neutral backing. */
[data-theme="quest"] .flxn-see-all {
background-color: var(--quest-surface);
border: 1px solid var(--quest-frame);
color: var(--mantine-primary-color-light-color);
font-weight: 600;
}
[data-theme="quest"] .flxn-see-all::before {
content: "";
display: inline-block;
width: 0.6em;
height: 0.6em;
margin-right: 0.45em;
border-radius: 50%;
vertical-align: baseline;
background-image: radial-gradient(
circle at 35% 30%,
color-mix(in srgb, var(--mantine-primary-color-filled) 55%, white) 0%,
var(--mantine-primary-color-filled) 45%,
color-mix(in srgb, var(--mantine-primary-color-filled) 55%, black) 100%
);
box-shadow: 0 1px 1px var(--flxn-pop-shadow);
}
@media (hover: hover) {
[data-theme="quest"] .flxn-see-all:hover {
background-color: var(--mantine-primary-color-light);
text-decoration: none;
}
}
/* Match card + reaction tray: the entry and its reward line share one
frame — same 2px bronze border, tray on deeper parchment. */
[data-theme="quest"] .flxn-reaction-row {
background-color: var(--quest-recessed);
border: 2px solid var(--quest-frame);
}
/* Badge tiles: seal-stamped keepsakes. */
[data-theme="quest"] .flxn-badge-tile {
box-shadow: var(--mantine-shadow-xs);
}
/* Avatars: party-member portrait medallions (ring via --flxn-avatar-*). */
[data-theme="quest"] .flxn-avatar-frame {
box-shadow: 0 1px 2px var(--flxn-pop-shadow);
}
+37
View File
@@ -0,0 +1,37 @@
import type { ThemeManifest } from "./types";
// Themes are auto-discovered: each src/themes/<id>/manifest.ts default-export
// is registered here. Keep this module CSS-free so server fns can import it;
// theme stylesheets are aggregated separately in ./styles.ts.
const modules = import.meta.glob<{ default: ThemeManifest }>("./*/manifest.ts", {
eager: true,
});
export const THEMES: Record<string, ThemeManifest> = {};
for (const [path, mod] of Object.entries(modules)) {
const folder = path.split("/")[1];
const manifest = mod.default;
if (!manifest || manifest.id !== folder) {
console.warn(`[themes] skipping ${path}: manifest id must equal folder name`);
continue;
}
THEMES[manifest.id] = manifest;
}
export const DEFAULT_THEME_ID = "default";
if (!THEMES[DEFAULT_THEME_ID]) {
throw new Error("[themes] default theme manifest is missing or invalid");
}
export const THEME_IDS = Object.keys(THEMES);
// Base order: default first, rest by id. Pickers re-sort by LOCALIZED title
// at render time (labels are i18n descriptors, unresolvable here).
export const THEME_LIST: ThemeManifest[] = Object.values(THEMES).sort(
(a, b) => (a.order ?? 100) - (b.order ?? 100) || a.id.localeCompare(b.id)
);
export const getTheme = (id?: string | null): ThemeManifest =>
(id ? THEMES[id] : undefined) ?? THEMES[DEFAULT_THEME_ID];
+24
View File
@@ -0,0 +1,24 @@
import { msg } from "@lingui/core/macro";
import type { ThemeManifest } from "../types";
const manifest: ThemeManifest = {
id: "seventies",
label: msg`Groovy`,
colorSchemes: "both",
fontLinks: [
"https://fonts.googleapis.com/css2?family=Righteous&display=swap",
"https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,500;0,600;0,700;1,400&display=swap",
],
chromeColors: {
light: { base: "#f6ecd9", dimmed: "#e4d5b8" },
dark: { base: "#241a12", dimmed: "#171009" },
},
mantine: {
// Mustard/avocado fills are mid-luminance; without autoContrast the
// yellow/lime accents render white-on-mustard at ~2-3.5:1.
autoContrast: true,
black: "#3f2d20",
},
};
export default manifest;
+728
View File
@@ -0,0 +1,728 @@
/* ============================================================
seventies — "Groovy": 1974 warm earth-tone lounge.
Cream + chocolate, the classic quad (mustard / burnt orange /
avocado / harvest brown), chunky radii, 2px chocolate borders,
sunburst stripe bands, contour-line arcs on cards.
============================================================ */
/* ---------- 1. Scheme-independent tokens — (0,2,0) ---------- */
[data-theme="seventies"][data-theme] {
/* type */
--mantine-font-family: "Poppins", "Trebuchet MS", "Verdana", ui-sans-serif,
sans-serif;
--mantine-font-family-headings: "Righteous", "Poppins", "Trebuchet MS",
ui-sans-serif, sans-serif;
--mantine-heading-font-weight: 400;
/* chunky rounded shape */
--mantine-radius-xs: 0.375rem;
--mantine-radius-sm: 0.5rem;
--mantine-radius-md: 0.75rem;
--mantine-radius-lg: 1rem;
--mantine-radius-xl: 1.5rem;
--mantine-radius-default: var(--mantine-radius-lg);
/* flat / soft warm shadows */
--mantine-shadow-xs: 0 1px 0 rgba(63, 45, 32, 0.08),
0 2px 6px rgba(63, 45, 32, 0.06);
--mantine-shadow-sm: 0 2px 0 rgba(63, 45, 32, 0.1),
0 4px 12px -2px rgba(63, 45, 32, 0.1);
--mantine-shadow-md: 0 3px 0 rgba(63, 45, 32, 0.1),
0 8px 20px -6px rgba(63, 45, 32, 0.16);
--mantine-shadow-lg: 0 4px 0 rgba(63, 45, 32, 0.1),
0 14px 32px -10px rgba(63, 45, 32, 0.2);
--mantine-shadow-xl: 0 5px 0 rgba(63, 45, 32, 0.12),
0 22px 44px -12px rgba(63, 45, 32, 0.24);
/* sunburst stripe band: mustard / burnt orange / harvest brown / avocado */
--seventies-band: linear-gradient(
to bottom,
#e3a92e 0 25%,
#c9552e 25% 50%,
#7a4a21 50% 75%,
#7a8450 75% 100%
);
/* app hook shape tokens */
--flxn-avatar-radius: 30%;
--flxn-avatar-border: 2px solid var(--mantine-color-default-border);
--flxn-badge-radius: 14px;
/* --- retuned ramps (warm-shifted 1974 palette) --- */
/* yellow → mustard (#e3a92e) */
--mantine-color-yellow-0: #fbf3dc;
--mantine-color-yellow-1: #f5e7bf;
--mantine-color-yellow-2: #efd899;
--mantine-color-yellow-3: #e9c46b;
--mantine-color-yellow-4: #e6b544;
--mantine-color-yellow-5: #e3a92e;
--mantine-color-yellow-6: #cd9322;
--mantine-color-yellow-7: #ad7a1c;
--mantine-color-yellow-8: #8c6216;
--mantine-color-yellow-9: #6b4a11;
/* orange → burnt orange (#c9552e) */
--mantine-color-orange-0: #fbeee6;
--mantine-color-orange-1: #f6dcca;
--mantine-color-orange-2: #efc2a4;
--mantine-color-orange-3: #e5a179;
--mantine-color-orange-4: #d87b50;
--mantine-color-orange-5: #c9552e;
--mantine-color-orange-6: #b64826;
--mantine-color-orange-7: #973b20;
--mantine-color-orange-8: #782f1a;
--mantine-color-orange-9: #5c2414;
/* red → cherry brick (crimson, kept off burnt orange's hue) */
--mantine-color-red-0: #fae9e9;
--mantine-color-red-1: #f2cfcf;
--mantine-color-red-2: #e8abab;
--mantine-color-red-3: #db8282;
--mantine-color-red-4: #ca5b5b;
--mantine-color-red-5: #b83a3a;
--mantine-color-red-6: #a32f2f;
--mantine-color-red-7: #872626;
--mantine-color-red-8: #6b1e1e;
--mantine-color-red-9: #511717;
/* green → avocado (#7a8450) */
--mantine-color-green-0: #f3f4e7;
--mantine-color-green-1: #e5e7cb;
--mantine-color-green-2: #d1d5a9;
--mantine-color-green-3: #b9bf85;
--mantine-color-green-4: #9aa268;
--mantine-color-green-5: #7a8450;
--mantine-color-green-6: #6a7343;
--mantine-color-green-7: #575e36;
--mantine-color-green-8: #444a2a;
--mantine-color-green-9: #32361f;
/* lime → olive chartreuse */
--mantine-color-lime-0: #f6f6e2;
--mantine-color-lime-1: #ecedc2;
--mantine-color-lime-2: #dee09b;
--mantine-color-lime-3: #cbcf72;
--mantine-color-lime-4: #b5bb50;
--mantine-color-lime-5: #9da437;
--mantine-color-lime-6: #888f2c;
--mantine-color-lime-7: #6f7423;
--mantine-color-lime-8: #575b1b;
--mantine-color-lime-9: #404313;
/* teal → lounge teal */
--mantine-color-teal-0: #e9f2ef;
--mantine-color-teal-1: #cfe3dd;
--mantine-color-teal-2: #adcec5;
--mantine-color-teal-3: #86b3a7;
--mantine-color-teal-4: #619789;
--mantine-color-teal-5: #467d6f;
--mantine-color-teal-6: #3a6b5f;
--mantine-color-teal-7: #2f574d;
--mantine-color-teal-8: #24443c;
--mantine-color-teal-9: #1a322c;
/* cyan → dusty aqua */
--mantine-color-cyan-0: #e9f2f2;
--mantine-color-cyan-1: #cfe3e2;
--mantine-color-cyan-2: #accccb;
--mantine-color-cyan-3: #85b0af;
--mantine-color-cyan-4: #629392;
--mantine-color-cyan-5: #4a7c7a;
--mantine-color-cyan-6: #3e6a69;
--mantine-color-cyan-7: #325655;
--mantine-color-cyan-8: #274343;
--mantine-color-cyan-9: #1c3131;
/* blue → petrol denim */
--mantine-color-blue-0: #eaf0f3;
--mantine-color-blue-1: #d1dee4;
--mantine-color-blue-2: #b0c5cf;
--mantine-color-blue-3: #89a7b5;
--mantine-color-blue-4: #648b9c;
--mantine-color-blue-5: #4b7386;
--mantine-color-blue-6: #3f6274;
--mantine-color-blue-7: #33505f;
--mantine-color-blue-8: #283e4a;
--mantine-color-blue-9: #1d2d36;
/* indigo → deep denim */
--mantine-color-indigo-0: #eaeef4;
--mantine-color-indigo-1: #d3dbe6;
--mantine-color-indigo-2: #b4c1d3;
--mantine-color-indigo-3: #8fa2bb;
--mantine-color-indigo-4: #6d85a3;
--mantine-color-indigo-5: #546d8c;
--mantine-color-indigo-6: #475d79;
--mantine-color-indigo-7: #3a4c63;
--mantine-color-indigo-8: #2d3b4d;
--mantine-color-indigo-9: #212b38;
/* violet → plum */
--mantine-color-violet-0: #f1edf2;
--mantine-color-violet-1: #e0d7e2;
--mantine-color-violet-2: #c9bacc;
--mantine-color-violet-3: #ad97b1;
--mantine-color-violet-4: #917797;
--mantine-color-violet-5: #7a5f80;
--mantine-color-violet-6: #695070;
--mantine-color-violet-7: #56415c;
--mantine-color-violet-8: #443348;
--mantine-color-violet-9: #322535;
/* grape → aubergine */
--mantine-color-grape-0: #f4ecf1;
--mantine-color-grape-1: #e6d4df;
--mantine-color-grape-2: #d2b3c6;
--mantine-color-grape-3: #b98da7;
--mantine-color-grape-4: #a06a8b;
--mantine-color-grape-5: #8b5175;
--mantine-color-grape-6: #794464;
--mantine-color-grape-7: #633751;
--mantine-color-grape-8: #4e2b40;
--mantine-color-grape-9: #3a202f;
/* pink → dusty rose (rosier than red so the pair stays distinct) */
--mantine-color-pink-0: #f9edf0;
--mantine-color-pink-1: #f1d5dc;
--mantine-color-pink-2: #e6b3c0;
--mantine-color-pink-3: #d78da0;
--mantine-color-pink-4: #c66781;
--mantine-color-pink-5: #b24a66;
--mantine-color-pink-6: #9e3e59;
--mantine-color-pink-7: #83334a;
--mantine-color-pink-8: #682839;
--mantine-color-pink-9: #4e1e2b;
/* gray → warm taupe */
--mantine-color-gray-0: #faf4e6;
--mantine-color-gray-1: #f1e6cf;
--mantine-color-gray-2: #e4d5b8;
--mantine-color-gray-3: #d0bd9c;
--mantine-color-gray-4: #b5a083;
--mantine-color-gray-5: #97826a;
--mantine-color-gray-6: #7a6753;
--mantine-color-gray-7: #5f4e3e;
--mantine-color-gray-8: #47392c;
--mantine-color-gray-9: #30251b;
/* dark → espresso */
--mantine-color-dark-0: #efe3d3;
--mantine-color-dark-1: #d9c6ad;
--mantine-color-dark-2: #b39a7d;
--mantine-color-dark-3: #8a7258;
--mantine-color-dark-4: #5c4732;
--mantine-color-dark-5: #4a3826;
--mantine-color-dark-6: #33261a;
--mantine-color-dark-7: #241a12;
--mantine-color-dark-8: #1b130d;
--mantine-color-dark-9: #120c08;
}
/* ---------- 2. Per-scheme tokens — (0,3,0) ---------- */
[data-theme="seventies"][data-theme][data-mantine-color-scheme="light"] {
--mantine-color-body: #f6ecd9;
--mantine-color-text: #3f2d20;
--mantine-color-bright: #241a12;
--mantine-color-dimmed: #7a6753;
--mantine-color-placeholder: #97826a;
--mantine-color-anchor: var(--mantine-primary-color-filled);
--mantine-color-error: var(--mantine-color-red-6);
--mantine-color-default: #fdf7ea;
--mantine-color-default-hover: #f3e6c9;
--mantine-color-default-color: #3f2d20;
--mantine-color-default-border: #3f2d20;
--seventies-ink: #3f2d20;
--seventies-surface: #fdf7ea;
--seventies-tray: #f0e2c3;
--seventies-contour: rgba(63, 45, 32, 0.07);
/* alpha tints re-derived from the retuned ramps */
--mantine-color-yellow-light: color-mix(in srgb, var(--mantine-color-yellow-6) 12%, transparent);
--mantine-color-yellow-light-hover: color-mix(in srgb, var(--mantine-color-yellow-6) 16%, transparent);
--mantine-color-orange-light: color-mix(in srgb, var(--mantine-color-orange-6) 10%, transparent);
--mantine-color-orange-light-hover: color-mix(in srgb, var(--mantine-color-orange-6) 14%, transparent);
--mantine-color-red-light: color-mix(in srgb, var(--mantine-color-red-6) 10%, transparent);
--mantine-color-red-light-hover: color-mix(in srgb, var(--mantine-color-red-6) 14%, transparent);
--mantine-color-green-light: color-mix(in srgb, var(--mantine-color-green-6) 11%, transparent);
--mantine-color-green-light-hover: color-mix(in srgb, var(--mantine-color-green-6) 15%, transparent);
--mantine-color-lime-light: color-mix(in srgb, var(--mantine-color-lime-6) 11%, transparent);
--mantine-color-lime-light-hover: color-mix(in srgb, var(--mantine-color-lime-6) 15%, transparent);
--mantine-color-teal-light: color-mix(in srgb, var(--mantine-color-teal-6) 10%, transparent);
--mantine-color-teal-light-hover: color-mix(in srgb, var(--mantine-color-teal-6) 14%, transparent);
--mantine-color-cyan-light: color-mix(in srgb, var(--mantine-color-cyan-6) 10%, transparent);
--mantine-color-cyan-light-hover: color-mix(in srgb, var(--mantine-color-cyan-6) 14%, transparent);
--mantine-color-blue-light: color-mix(in srgb, var(--mantine-color-blue-6) 10%, transparent);
--mantine-color-blue-light-hover: color-mix(in srgb, var(--mantine-color-blue-6) 14%, transparent);
--mantine-color-indigo-light: color-mix(in srgb, var(--mantine-color-indigo-6) 10%, transparent);
--mantine-color-indigo-light-hover: color-mix(in srgb, var(--mantine-color-indigo-6) 14%, transparent);
--mantine-color-violet-light: color-mix(in srgb, var(--mantine-color-violet-6) 10%, transparent);
--mantine-color-violet-light-hover: color-mix(in srgb, var(--mantine-color-violet-6) 14%, transparent);
--mantine-color-grape-light: color-mix(in srgb, var(--mantine-color-grape-6) 10%, transparent);
--mantine-color-grape-light-hover: color-mix(in srgb, var(--mantine-color-grape-6) 14%, transparent);
--mantine-color-pink-light: color-mix(in srgb, var(--mantine-color-pink-6) 10%, transparent);
--mantine-color-pink-light-hover: color-mix(in srgb, var(--mantine-color-pink-6) 14%, transparent);
--mantine-color-gray-light: color-mix(in srgb, var(--mantine-color-gray-6) 10%, transparent);
--mantine-color-gray-light-hover: color-mix(in srgb, var(--mantine-color-gray-6) 14%, transparent);
/* outline-hover tints for the pickable accents (Mantine's are stock-hue rgba) */
--mantine-color-blue-outline-hover: color-mix(in srgb, var(--mantine-color-blue-6) 6%, transparent);
--mantine-color-red-outline-hover: color-mix(in srgb, var(--mantine-color-red-6) 6%, transparent);
--mantine-color-green-outline-hover: color-mix(in srgb, var(--mantine-color-green-6) 6%, transparent);
--mantine-color-yellow-outline-hover: color-mix(in srgb, var(--mantine-color-yellow-6) 6%, transparent);
--mantine-color-grape-outline-hover: color-mix(in srgb, var(--mantine-color-grape-6) 6%, transparent);
--mantine-color-orange-outline-hover: color-mix(in srgb, var(--mantine-color-orange-6) 6%, transparent);
--mantine-color-pink-outline-hover: color-mix(in srgb, var(--mantine-color-pink-6) 6%, transparent);
--mantine-color-lime-outline-hover: color-mix(in srgb, var(--mantine-color-lime-6) 6%, transparent);
/* app tokens */
--flxn-gold: #d3931b;
--flxn-silver: #a08a70;
--flxn-bronze: #b05c33;
--flxn-toast-success: var(--mantine-color-green-6);
--flxn-toast-error: var(--mantine-color-red-6);
--flxn-showcase-bg: #fdf7ea;
--flxn-overlay: rgba(36, 26, 18, 0.45);
--flxn-dotgrid-color: rgba(63, 45, 32, 0.22);
--flxn-pop-shadow: rgba(63, 45, 32, 0.35);
/* wood-paneled podium tiers */
--flxn-podium-1-bg: repeating-linear-gradient(
90deg,
rgba(63, 45, 32, 0.06) 0 2px,
transparent 2px 12px
),
linear-gradient(180deg, #f2dca6, #e9c878);
--flxn-podium-1-border: #b07c1c;
--flxn-podium-1-label: #6b4a11;
--flxn-podium-2-bg: repeating-linear-gradient(
90deg,
rgba(63, 45, 32, 0.06) 0 2px,
transparent 2px 12px
),
linear-gradient(180deg, #eee0c8, #e0cba6);
--flxn-podium-2-border: #97826a;
--flxn-podium-2-label: #5f4e3e;
--flxn-podium-3-bg: repeating-linear-gradient(
90deg,
rgba(63, 45, 32, 0.06) 0 2px,
transparent 2px 12px
),
linear-gradient(180deg, #f2cdb4, #e6b18c);
--flxn-podium-3-border: #a2542f;
--flxn-podium-3-label: #782f1a;
}
[data-theme="seventies"][data-theme][data-mantine-color-scheme="dark"] {
--mantine-color-body: #241a12;
--mantine-color-text: #f2e6d0;
--mantine-color-bright: #fdf6e4;
--mantine-color-dimmed: #b39a7d;
--mantine-color-placeholder: #8a7258;
--mantine-color-anchor: var(--mantine-primary-color-light-color);
--mantine-color-error: var(--mantine-color-red-3);
--mantine-color-default: #33261a;
--mantine-color-default-hover: #40301f;
--mantine-color-default-color: #f2e6d0;
--mantine-color-default-border: #5f4832;
--seventies-ink: #f2e6d0;
--seventies-surface: #2c2015;
--seventies-tray: #362818;
--seventies-contour: rgba(242, 230, 208, 0.06);
/* filled variants stay mid-ramp (default shade 8 vanishes on espresso);
primary aliases follow via Mantine's var indirection, so the user's
accent picks these up automatically */
--mantine-color-yellow-filled: var(--mantine-color-yellow-5);
--mantine-color-yellow-filled-hover: var(--mantine-color-yellow-4);
--mantine-color-orange-filled: var(--mantine-color-orange-6);
--mantine-color-orange-filled-hover: var(--mantine-color-orange-5);
--mantine-color-red-filled: var(--mantine-color-red-6);
--mantine-color-red-filled-hover: var(--mantine-color-red-5);
--mantine-color-green-filled: var(--mantine-color-green-6);
--mantine-color-green-filled-hover: var(--mantine-color-green-5);
--mantine-color-lime-filled: var(--mantine-color-lime-6);
--mantine-color-lime-filled-hover: var(--mantine-color-lime-5);
--mantine-color-teal-filled: var(--mantine-color-teal-6);
--mantine-color-teal-filled-hover: var(--mantine-color-teal-5);
--mantine-color-cyan-filled: var(--mantine-color-cyan-6);
--mantine-color-cyan-filled-hover: var(--mantine-color-cyan-5);
--mantine-color-blue-filled: var(--mantine-color-blue-6);
--mantine-color-blue-filled-hover: var(--mantine-color-blue-5);
--mantine-color-indigo-filled: var(--mantine-color-indigo-6);
--mantine-color-indigo-filled-hover: var(--mantine-color-indigo-5);
--mantine-color-violet-filled: var(--mantine-color-violet-6);
--mantine-color-violet-filled-hover: var(--mantine-color-violet-5);
--mantine-color-grape-filled: var(--mantine-color-grape-6);
--mantine-color-grape-filled-hover: var(--mantine-color-grape-5);
--mantine-color-pink-filled: var(--mantine-color-pink-6);
--mantine-color-pink-filled-hover: var(--mantine-color-pink-5);
/* alpha tints + tint text colors */
--mantine-color-yellow-light: color-mix(in srgb, var(--mantine-color-yellow-4) 16%, transparent);
--mantine-color-yellow-light-hover: color-mix(in srgb, var(--mantine-color-yellow-4) 22%, transparent);
--mantine-color-yellow-light-color: var(--mantine-color-yellow-3);
--mantine-color-orange-light: color-mix(in srgb, var(--mantine-color-orange-4) 16%, transparent);
--mantine-color-orange-light-hover: color-mix(in srgb, var(--mantine-color-orange-4) 22%, transparent);
--mantine-color-orange-light-color: var(--mantine-color-orange-3);
--mantine-color-red-light: color-mix(in srgb, var(--mantine-color-red-4) 16%, transparent);
--mantine-color-red-light-hover: color-mix(in srgb, var(--mantine-color-red-4) 22%, transparent);
--mantine-color-red-light-color: var(--mantine-color-red-3);
--mantine-color-green-light: color-mix(in srgb, var(--mantine-color-green-4) 16%, transparent);
--mantine-color-green-light-hover: color-mix(in srgb, var(--mantine-color-green-4) 22%, transparent);
--mantine-color-green-light-color: var(--mantine-color-green-3);
--mantine-color-lime-light: color-mix(in srgb, var(--mantine-color-lime-4) 16%, transparent);
--mantine-color-lime-light-hover: color-mix(in srgb, var(--mantine-color-lime-4) 22%, transparent);
--mantine-color-lime-light-color: var(--mantine-color-lime-3);
--mantine-color-teal-light: color-mix(in srgb, var(--mantine-color-teal-4) 16%, transparent);
--mantine-color-teal-light-hover: color-mix(in srgb, var(--mantine-color-teal-4) 22%, transparent);
--mantine-color-teal-light-color: var(--mantine-color-teal-3);
--mantine-color-cyan-light: color-mix(in srgb, var(--mantine-color-cyan-4) 16%, transparent);
--mantine-color-cyan-light-hover: color-mix(in srgb, var(--mantine-color-cyan-4) 22%, transparent);
--mantine-color-cyan-light-color: var(--mantine-color-cyan-3);
--mantine-color-blue-light: color-mix(in srgb, var(--mantine-color-blue-4) 16%, transparent);
--mantine-color-blue-light-hover: color-mix(in srgb, var(--mantine-color-blue-4) 22%, transparent);
--mantine-color-blue-light-color: var(--mantine-color-blue-3);
--mantine-color-indigo-light: color-mix(in srgb, var(--mantine-color-indigo-4) 16%, transparent);
--mantine-color-indigo-light-hover: color-mix(in srgb, var(--mantine-color-indigo-4) 22%, transparent);
--mantine-color-indigo-light-color: var(--mantine-color-indigo-3);
--mantine-color-violet-light: color-mix(in srgb, var(--mantine-color-violet-4) 16%, transparent);
--mantine-color-violet-light-hover: color-mix(in srgb, var(--mantine-color-violet-4) 22%, transparent);
--mantine-color-violet-light-color: var(--mantine-color-violet-3);
--mantine-color-grape-light: color-mix(in srgb, var(--mantine-color-grape-4) 16%, transparent);
--mantine-color-grape-light-hover: color-mix(in srgb, var(--mantine-color-grape-4) 22%, transparent);
--mantine-color-grape-light-color: var(--mantine-color-grape-3);
--mantine-color-pink-light: color-mix(in srgb, var(--mantine-color-pink-4) 16%, transparent);
--mantine-color-pink-light-hover: color-mix(in srgb, var(--mantine-color-pink-4) 22%, transparent);
--mantine-color-pink-light-color: var(--mantine-color-pink-3);
--mantine-color-gray-light: color-mix(in srgb, var(--mantine-color-gray-4) 14%, transparent);
--mantine-color-gray-light-hover: color-mix(in srgb, var(--mantine-color-gray-4) 20%, transparent);
--mantine-color-gray-light-color: var(--mantine-color-gray-2);
/* outline-hover tints for the pickable accents (Mantine's are stock-hue rgba) */
--mantine-color-blue-outline-hover: color-mix(in srgb, var(--mantine-color-blue-4) 10%, transparent);
--mantine-color-red-outline-hover: color-mix(in srgb, var(--mantine-color-red-4) 10%, transparent);
--mantine-color-green-outline-hover: color-mix(in srgb, var(--mantine-color-green-4) 10%, transparent);
--mantine-color-yellow-outline-hover: color-mix(in srgb, var(--mantine-color-yellow-4) 10%, transparent);
--mantine-color-grape-outline-hover: color-mix(in srgb, var(--mantine-color-grape-4) 10%, transparent);
--mantine-color-orange-outline-hover: color-mix(in srgb, var(--mantine-color-orange-4) 10%, transparent);
--mantine-color-pink-outline-hover: color-mix(in srgb, var(--mantine-color-pink-4) 10%, transparent);
--mantine-color-lime-outline-hover: color-mix(in srgb, var(--mantine-color-lime-4) 10%, transparent);
/* app tokens */
--flxn-gold: #e6b544;
--flxn-silver: #b5a083;
--flxn-bronze: #d87b50;
--flxn-toast-success: var(--mantine-color-green-3);
--flxn-toast-error: var(--mantine-color-red-3);
--flxn-showcase-bg: #2c2015;
--flxn-overlay: rgba(18, 12, 8, 0.6);
--flxn-dotgrid-color: rgba(242, 230, 208, 0.14);
--flxn-pop-shadow: rgba(0, 0, 0, 0.5);
--flxn-podium-1-bg: repeating-linear-gradient(
90deg,
rgba(0, 0, 0, 0.18) 0 2px,
transparent 2px 12px
),
linear-gradient(180deg, #4a3a1c, #3a2d15);
--flxn-podium-1-border: #a37a20;
--flxn-podium-1-label: #e6b544;
--flxn-podium-2-bg: repeating-linear-gradient(
90deg,
rgba(0, 0, 0, 0.18) 0 2px,
transparent 2px 12px
),
linear-gradient(180deg, #3d3123, #322718);
--flxn-podium-2-border: #6b5138;
--flxn-podium-2-label: #cbb391;
--flxn-podium-3-bg: repeating-linear-gradient(
90deg,
rgba(0, 0, 0, 0.18) 0 2px,
transparent 2px 12px
),
linear-gradient(180deg, #4a2c1c, #3a2115);
--flxn-podium-3-border: #8a4a28;
--flxn-podium-3-label: #e09a72;
}
/* ---------- 3. Component restyles ---------- */
/* app shell: warm surfaces, sunburst band under the header */
[data-theme="seventies"] .mantine-AppShell-header {
background-color: var(--seventies-surface);
border-bottom: none;
}
[data-theme="seventies"] .mantine-AppShell-header::after {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 5px;
background: var(--seventies-band);
pointer-events: none;
}
[data-theme="seventies"] .mantine-AppShell-navbar {
background-color: var(--seventies-surface);
border-inline-end: 2px solid var(--mantine-color-default-border);
}
/* surfaces: chunky 2px chocolate borders (covers cards, sheets, the
floating mobile nav pill, modal content — all Paper-based) */
[data-theme="seventies"] .mantine-Paper-root {
background-color: var(--seventies-surface);
border: 2px solid var(--mantine-color-default-border);
}
/* retro contour-line arcs in card corners */
[data-theme="seventies"] .mantine-Card-root {
position: relative;
}
[data-theme="seventies"] .mantine-Card-root::after {
content: "";
position: absolute;
top: 0;
right: 0;
width: 7rem;
height: 7rem;
pointer-events: none;
border-top-right-radius: calc(var(--mantine-radius-default) - 2px);
background: repeating-radial-gradient(
circle at 100% 0%,
var(--seventies-contour) 0 2px,
transparent 2px 14px
);
-webkit-mask-image: radial-gradient(circle at 100% 0%, #000 30%, transparent 72%);
mask-image: radial-gradient(circle at 100% 0%, #000 30%, transparent 72%);
}
/* buttons: solid warm fills with a darker chunky bottom edge */
[data-theme="seventies"] .mantine-Button-root {
font-weight: 600;
letter-spacing: 0.02em;
transition: box-shadow 120ms cubic-bezier(0.32, 0.72, 0, 1);
}
[data-theme="seventies"] .mantine-Button-root[data-variant="filled"],
[data-theme="seventies"] .mantine-Button-root[data-variant="light"],
[data-theme="seventies"] .mantine-Button-root[data-variant="default"] {
box-shadow: 0 3px 0 color-mix(in srgb, var(--button-bg) 60%, #120c08);
}
[data-theme="seventies"] .mantine-Button-root[data-variant="default"],
[data-theme="seventies"] .mantine-Button-root[data-variant="outline"] {
border-width: 2px;
}
[data-theme="seventies"] .mantine-Button-root[data-variant="filled"]:active:not(:disabled):not([data-disabled]):not([data-loading]),
[data-theme="seventies"] .mantine-Button-root[data-variant="light"]:active:not(:disabled):not([data-disabled]):not([data-loading]),
[data-theme="seventies"] .mantine-Button-root[data-variant="default"]:active:not(:disabled):not([data-disabled]):not([data-loading]) {
box-shadow: 0 1px 0 color-mix(in srgb, var(--button-bg) 60%, #120c08);
}
[data-theme="seventies"] .mantine-ActionIcon-root[data-variant="filled"],
[data-theme="seventies"] .mantine-ActionIcon-root[data-variant="light"],
[data-theme="seventies"] .mantine-ActionIcon-root[data-variant="default"] {
box-shadow: 0 2px 0 color-mix(in srgb, var(--ai-bg) 60%, #120c08);
}
[data-theme="seventies"] .mantine-ActionIcon-root[data-variant="filled"]:active:not(:disabled):not([data-disabled]):not([data-loading]),
[data-theme="seventies"] .mantine-ActionIcon-root[data-variant="light"]:active:not(:disabled):not([data-disabled]):not([data-loading]),
[data-theme="seventies"] .mantine-ActionIcon-root[data-variant="default"]:active:not(:disabled):not([data-disabled]):not([data-loading]) {
box-shadow: 0 0 0 transparent;
}
/* press feedback: sink instead of scale */
@media (prefers-reduced-motion: no-preference) {
[data-theme="seventies"] .flxn-press:active:not(:disabled):not([data-disabled]):not([data-loading]) {
transform: translateY(2px);
}
}
/* inputs */
[data-theme="seventies"] .mantine-Input-input {
border-width: 2px;
background-color: var(--seventies-surface);
font-weight: 500;
}
/* badges as retro patches: bold border + inner stitch line */
[data-theme="seventies"] .mantine-Badge-root {
position: relative;
border: 2px solid var(--mantine-color-default-border);
border-radius: var(--mantine-radius-sm);
font-weight: 700;
letter-spacing: 0.05em;
}
[data-theme="seventies"] .mantine-Badge-root::after {
content: "";
position: absolute;
inset: 2px;
border: 1px dashed color-mix(in srgb, currentColor 55%, transparent);
border-radius: calc(var(--mantine-radius-sm) - 3px);
pointer-events: none;
}
/* tabs + segmented control */
[data-theme="seventies"] .mantine-Tabs-tab {
font-weight: 600;
}
[data-theme="seventies"] .mantine-Tabs-tab[data-active] {
border-color: var(--mantine-primary-color-filled);
}
[data-theme="seventies"] .mantine-SegmentedControl-root {
background-color: color-mix(in srgb, var(--seventies-ink) 8%, var(--mantine-color-body));
border: 2px solid var(--mantine-color-default-border);
}
[data-theme="seventies"] .mantine-SegmentedControl-indicator {
background-color: light-dark(#e9c46b, #8c6216);
box-shadow: 0 2px 0 rgba(18, 12, 8, 0.25);
}
/* overlays & floating surfaces */
[data-theme="seventies"] .mantine-Modal-header {
background-color: var(--seventies-surface);
position: relative;
}
[data-theme="seventies"] .mantine-Modal-header::after {
content: "";
position: absolute;
left: var(--mantine-spacing-md);
right: var(--mantine-spacing-md);
bottom: 0;
height: 4px;
border-radius: 2px;
background: var(--seventies-band);
pointer-events: none;
}
[data-theme="seventies"] .mantine-Popover-dropdown {
background-color: var(--seventies-surface);
border: 2px solid var(--mantine-color-default-border);
box-shadow: var(--mantine-shadow-md);
}
[data-theme="seventies"] .mantine-Tooltip-tooltip {
background-color: #33261a;
color: #f2e6d0;
border-radius: var(--mantine-radius-sm);
}
[data-theme="seventies"] .mantine-Notification-root {
background-color: var(--seventies-surface);
border: 2px solid var(--mantine-color-default-border);
box-shadow: var(--mantine-shadow-md);
}
[data-theme="seventies"] .mantine-Alert-root {
border: 2px solid var(--mantine-color-default-border);
}
/* controls */
[data-theme="seventies"] .mantine-Switch-track {
border: 2px solid var(--mantine-color-default-border);
}
[data-theme="seventies"] .mantine-Checkbox-input {
border: 2px solid var(--mantine-color-default-border);
}
[data-theme="seventies"] .mantine-Progress-root {
background-color: color-mix(in srgb, var(--seventies-ink) 12%, transparent);
}
/* ---------- 4. App component hooks ---------- */
/* tab strip: docks as a cream strip closed off by a chocolate rule */
[data-theme="seventies"] .flxn-tabs[data-stuck] {
background-color: var(--seventies-surface);
border-bottom: 2px solid var(--mantine-color-default-border);
}
[data-theme="seventies"] .flxn-tab {
font-weight: 600;
color: var(--mantine-color-dimmed);
}
[data-theme="seventies"] .flxn-tab[data-active] {
color: light-dark(
var(--mantine-primary-color-filled),
var(--mantine-primary-color-light-color)
);
font-weight: 700;
}
[data-theme="seventies"] .flxn-tabs-indicator {
border-bottom: 3px solid var(--mantine-primary-color-filled);
border-radius: 3px;
}
/* nav tiles: chunky bordered cards with a chocolate ledge; sink on press */
[data-theme="seventies"] .flxn-tile {
background-color: var(--seventies-surface);
border: 2px solid var(--mantine-color-default-border);
box-shadow: 0 3px 0 var(--mantine-color-default-border);
transition: box-shadow 120ms cubic-bezier(0.32, 0.72, 0, 1);
}
[data-theme="seventies"] .flxn-tile:active {
box-shadow: 0 1px 0 var(--mantine-color-default-border);
}
[data-theme="seventies"] .flxn-tile-icon {
color: light-dark(
var(--mantine-primary-color-filled),
var(--mantine-primary-color-light-color)
);
}
/* "see all" as a sew-on patch chip: light accent wash + wash text */
[data-theme="seventies"] .flxn-see-all {
position: relative;
border: 2px solid var(--mantine-color-default-border);
border-radius: var(--mantine-radius-sm);
background-color: var(--mantine-primary-color-light);
color: var(--mantine-primary-color-light-color);
font-weight: 700;
letter-spacing: 0.04em;
}
[data-theme="seventies"] .flxn-see-all::after {
content: "";
position: absolute;
inset: 2px;
border: 1px dashed color-mix(in srgb, currentColor 55%, transparent);
border-radius: calc(var(--mantine-radius-sm) - 3px);
pointer-events: none;
}
/* match card + reaction tray: one construction — matching chocolate
borders, tray reads as a wood-panel shelf under the card */
[data-theme="seventies"] .flxn-match-card {
background-color: var(--seventies-surface);
border: 2px solid var(--mantine-color-default-border);
}
[data-theme="seventies"] .flxn-reaction-row {
background: repeating-linear-gradient(
90deg,
color-mix(in srgb, var(--seventies-ink) 7%, transparent) 0 2px,
transparent 2px 12px
),
var(--seventies-tray);
border: 2px solid var(--mantine-color-default-border);
border-radius: var(--mantine-radius-md);
}
+17
View File
@@ -0,0 +1,17 @@
import { msg } from "@lingui/core/macro";
import type { ThemeManifest } from "../types";
const manifest: ThemeManifest = {
id: "sketch",
label: msg`Sketchbook`,
colorSchemes: "both",
fontLinks: [
"https://fonts.googleapis.com/css2?family=Caveat:wght@500;600;700&family=Patrick+Hand&display=swap",
],
chromeColors: {
light: { base: "#f9f4e8", dimmed: "#e9e1cf" },
dark: { base: "#232a26", dimmed: "#1a201d" },
},
};
export default manifest;
+749
View File
@@ -0,0 +1,749 @@
/* Sketchbook — hand-drawn ink on warm paper (light) / chalk on slate (dark).
The look is carried by 2px "pen" borders with asymmetric wobbly radii
(varied per component so each element reads as individually drawn), a faint
feTurbulence grain overlay, and watercolor washes derived from the user's
accent via color-mix. No SVG displacement filters (perf). */
/* ---------------------------------------------------------------- */
/* 1. Scheme-independent tokens */
/* ---------------------------------------------------------------- */
[data-theme="sketch"][data-theme] {
--mantine-font-family: "Patrick Hand", "Segoe Print", "Comic Sans MS", cursive;
--mantine-font-family-headings: "Caveat", "Patrick Hand", cursive;
--mantine-heading-font-weight: 600;
/* Caveat runs small for its em box — headings get larger sizes */
--mantine-h1-font-size: 2.75rem;
--mantine-h1-line-height: 1.15;
--mantine-h2-font-size: 2.25rem;
--mantine-h2-line-height: 1.18;
--mantine-h3-font-size: 1.85rem;
--mantine-h3-line-height: 1.2;
--mantine-h4-font-size: 1.55rem;
--mantine-h4-line-height: 1.25;
--mantine-h5-font-size: 1.3rem;
--mantine-h5-line-height: 1.3;
--mantine-h6-font-size: 1.15rem;
--mantine-h6-line-height: 1.35;
--mantine-radius-xs: 6px;
--mantine-radius-sm: 8px;
--mantine-radius-md: 10px;
--mantine-radius-lg: 14px;
--mantine-radius-xl: 20px;
--mantine-radius-default: 10px;
--flxn-avatar-radius: 42%; /* hand-drawn "circle" */
--flxn-avatar-border: 2px solid var(--sk-ink); /* single pen ring on the frame */
--flxn-badge-radius: 14px;
/* pencil-smudge shadows: soft, gray, slightly offset */
--mantine-shadow-xs: 0 1px 2px var(--sk-smudge);
--mantine-shadow-sm: 1px 2px 5px var(--sk-smudge);
--mantine-shadow-md: 2px 3px 9px var(--sk-smudge);
--mantine-shadow-lg: 3px 5px 14px var(--sk-smudge);
--mantine-shadow-xl: 4px 7px 20px var(--sk-smudge);
}
/* ---------------------------------------------------------------- */
/* 2a. Light — ink on warm paper */
/* ---------------------------------------------------------------- */
[data-theme="sketch"][data-theme][data-mantine-color-scheme="light"] {
--sk-ink: #333333;
--sk-ink-soft: rgba(51, 51, 51, 0.55);
--sk-on-fill: #2f2c26;
--sk-paper: #f9f4e8;
--sk-paper-raised: #fffdf6;
--sk-wash: color-mix(in srgb, var(--mantine-primary-color-6) 18%, #fffdf6);
--sk-accent-ink: color-mix(in srgb, var(--mantine-primary-color-6) 65%, #333333);
--sk-smudge: rgba(88, 80, 62, 0.2);
--sk-hatch: rgba(51, 51, 51, 0.22);
--sk-skeleton: #ebe3d0;
--sk-squiggle: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='5' viewBox='0 0 22 5'%3E%3Cpath d='M0 3 Q 5.5 0.8 11 3 T 22 3' fill='none' stroke='%23333333' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
--mantine-color-body: #f9f4e8;
--mantine-color-text: #333333;
--mantine-color-bright: #1f1d18;
--mantine-color-dimmed: #6b655a;
--mantine-color-placeholder: #8a8375;
--mantine-color-anchor: var(--sk-accent-ink);
--mantine-color-error: #a44e46;
--mantine-color-default: #fffdf6;
--mantine-color-default-hover: #f1ead9;
--mantine-color-default-color: #333333;
--mantine-color-default-border: #333333;
--mantine-primary-color-contrast: #2f2c26;
/* app tokens */
--flxn-gold: #dca815; /* crayon yellow */
--flxn-silver: #8f8a80; /* pencil gray */
--flxn-bronze: #b0744a; /* crayon sienna */
--flxn-toast-success: #5f8f5f;
--flxn-toast-error: #c26e63;
--flxn-podium-1-bg: #f8eecb;
--flxn-podium-1-border: #8c7a35;
--flxn-podium-1-label: #7c6614;
--flxn-podium-2-bg: #f0ede4;
--flxn-podium-2-border: #77746c;
--flxn-podium-2-label: #5f5c54;
--flxn-podium-3-bg: #f4e5d3;
--flxn-podium-3-border: #8f6a45;
--flxn-podium-3-label: #82552e;
--flxn-showcase-bg: #fffdf6;
--flxn-overlay: rgba(56, 50, 38, 0.45);
--flxn-dotgrid-color: rgba(51, 51, 51, 0.3); /* pencil dots */
--flxn-pop-shadow: rgba(88, 80, 62, 0.3);
}
/* ---------------------------------------------------------------- */
/* 2b. Dark — chalk on slate */
/* ---------------------------------------------------------------- */
[data-theme="sketch"][data-theme][data-mantine-color-scheme="dark"] {
--sk-ink: #dfdcd0; /* chalk */
--sk-ink-soft: rgba(223, 220, 208, 0.5);
--sk-on-fill: #232a26;
--sk-paper: #232a26;
--sk-paper-raised: #2a322d;
--sk-wash: color-mix(in srgb, var(--mantine-primary-color-4) 22%, #2a322d);
--sk-accent-ink: color-mix(in srgb, var(--mantine-primary-color-3) 70%, #dfdcd0);
--sk-smudge: rgba(0, 0, 0, 0.45);
--sk-hatch: rgba(35, 42, 38, 0.4);
--sk-skeleton: #38423b;
--sk-squiggle: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='5' viewBox='0 0 22 5'%3E%3Cpath d='M0 3 Q 5.5 0.8 11 3 T 22 3' fill='none' stroke='%23dfdcd0' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
--mantine-color-body: #232a26;
--mantine-color-text: #e8e5da;
--mantine-color-bright: #f6f4ec;
--mantine-color-dimmed: #a3ac9f;
--mantine-color-placeholder: #8b948a;
--mantine-color-anchor: var(--sk-accent-ink);
--mantine-color-error: #e8a198;
--mantine-color-default: #2a322d;
--mantine-color-default-hover: #333c36;
--mantine-color-default-color: #e8e5da;
--mantine-color-default-border: #d7d4c6;
--mantine-primary-color-contrast: #232a26;
/* app tokens — dusty chalk pastels */
--flxn-gold: #ecd27a;
--flxn-silver: #b9beb3;
--flxn-bronze: #d9a06b;
--flxn-toast-success: #b3d7ae;
--flxn-toast-error: #eaa79b;
--flxn-podium-1-bg: color-mix(in srgb, #ecd27a 12%, #2a322d);
--flxn-podium-1-border: #cdb96b;
--flxn-podium-1-label: #e9d78f;
--flxn-podium-2-bg: color-mix(in srgb, #b9beb3 10%, #2a322d);
--flxn-podium-2-border: #a4a99e;
--flxn-podium-2-label: #c9cec2;
--flxn-podium-3-bg: color-mix(in srgb, #d9a06b 12%, #2a322d);
--flxn-podium-3-border: #c08f5c;
--flxn-podium-3-label: #e0b384;
--flxn-showcase-bg: #2a322d;
--flxn-overlay: rgba(6, 9, 8, 0.6);
--flxn-dotgrid-color: rgba(223, 220, 208, 0.22); /* chalk dots */
--flxn-pop-shadow: rgba(0, 0, 0, 0.55);
}
/* ---------------------------------------------------------------- */
/* 3. Paper grain / chalk dust — one fixed overlay, feTurbulence */
/* ---------------------------------------------------------------- */
[data-theme="sketch"] body::before {
content: "";
position: fixed;
inset: 0;
z-index: 9998;
pointer-events: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23g)'/%3E%3C/svg%3E");
background-repeat: repeat;
mix-blend-mode: multiply;
opacity: 0.05;
}
[data-theme="sketch"][data-mantine-color-scheme="dark"] body::before {
mix-blend-mode: screen;
opacity: 0.07;
}
/* ---------------------------------------------------------------- */
/* 4. Surfaces — every card its own doodle */
/* ---------------------------------------------------------------- */
[data-theme="sketch"] .mantine-Paper-root {
background-color: var(--sk-paper-raised);
border: 2px solid var(--sk-ink);
border-radius: 255px 15px 225px 15px / 15px 225px 15px 255px;
box-shadow: none;
}
[data-theme="sketch"] .mantine-Card-root {
border-radius: 15px 225px 15px 255px / 225px 15px 255px 15px;
}
[data-theme="sketch"] .mantine-Modal-content {
border-radius: 18px 225px 22px 255px / 255px 22px 225px 18px;
box-shadow: var(--mantine-shadow-xl);
}
[data-theme="sketch"] .mantine-Modal-header {
background-color: var(--sk-paper-raised);
}
[data-theme="sketch"] .mantine-Drawer-content {
border-radius: 20px 235px 16px 245px / 245px 16px 235px 20px;
box-shadow: var(--mantine-shadow-xl);
}
[data-theme="sketch"] .mantine-Popover-dropdown,
[data-theme="sketch"] .mantine-Menu-dropdown {
background-color: var(--sk-paper-raised);
border: 2px solid var(--sk-ink);
border-radius: 255px 25px 225px 22px / 22px 225px 25px 255px;
box-shadow: var(--mantine-shadow-md);
}
[data-theme="sketch"] .mantine-Menu-item {
border-radius: 215px 14px 195px 16px / 16px 195px 14px 215px;
}
[data-theme="sketch"] .mantine-Notification-root {
background-color: var(--sk-paper-raised);
border: 2px solid var(--sk-ink);
border-radius: 15px 245px 18px 235px / 235px 18px 245px 15px;
box-shadow: var(--mantine-shadow-md);
}
[data-theme="sketch"] .mantine-Alert-root {
border: 2px solid var(--sk-ink-soft);
border-radius: 20px 240px 16px 250px / 250px 16px 240px 20px;
}
[data-theme="sketch"] .mantine-Tooltip-tooltip {
background-color: var(--sk-ink);
color: var(--sk-paper);
border-radius: 205px 12px 225px 14px / 14px 225px 12px 205px;
}
/* ---------------------------------------------------------------- */
/* 5. Shell */
/* ---------------------------------------------------------------- */
[data-theme="sketch"] .mantine-AppShell-header {
border-bottom: 2px solid var(--sk-ink);
}
[data-theme="sketch"] .mantine-AppShell-navbar {
border-inline-end: 2px solid var(--sk-ink);
}
[data-theme="sketch"] .mantine-AppShell-aside {
border-inline-start: 2px solid var(--sk-ink);
}
[data-theme="sketch"] .mantine-Divider-root {
--divider-color: var(--sk-ink-soft);
--divider-size: 2px;
}
[data-theme="sketch"] .mantine-Divider-root[data-orientation="horizontal"] {
border-top-style: dashed;
}
[data-theme="sketch"] .mantine-Divider-root[data-orientation="vertical"] {
border-inline-start-style: dashed;
}
/* ---------------------------------------------------------------- */
/* 6. Buttons — sketched boxes */
/* ---------------------------------------------------------------- */
[data-theme="sketch"] .mantine-Button-root {
border-radius: 225px 20px 255px 15px / 15px 255px 20px 225px;
box-shadow: none;
font-weight: 400; /* Patrick Hand ships 400 only; avoid faux bold */
}
[data-theme="sketch"] .mantine-Button-root:where([data-variant="filled"], [data-variant="default"], [data-variant="outline"], [data-variant="light"], [data-variant="white"]) {
border: 2px solid var(--sk-ink);
}
[data-theme="sketch"] .mantine-ActionIcon-root {
border-radius: 160px 25px 185px 28px / 28px 185px 25px 160px;
box-shadow: none;
}
[data-theme="sketch"] .mantine-ActionIcon-root:where([data-variant="filled"], [data-variant="default"], [data-variant="outline"], [data-variant="light"], [data-variant="white"]) {
border: 2px solid var(--sk-ink);
}
[data-theme="sketch"] .mantine-Button-root[data-variant="filled"],
[data-theme="sketch"] .mantine-ActionIcon-root[data-variant="filled"],
[data-theme="sketch"] .mantine-Badge-root[data-variant="filled"],
[data-theme="sketch"] .mantine-ThemeIcon-root[data-variant="filled"] {
color: var(--sk-on-fill); /* ink on watercolor wash, chalkboard on pastel */
}
/* press feedback: a slight crooked stamp */
@media (prefers-reduced-motion: no-preference) {
[data-theme="sketch"] .flxn-press:active:not(:disabled):not([data-disabled]):not([data-loading]) {
transform: scale(0.96) rotate(-0.7deg);
}
}
/* ---------------------------------------------------------------- */
/* 7. Inputs */
/* ---------------------------------------------------------------- */
[data-theme="sketch"] .mantine-Input-input {
background-color: var(--sk-paper-raised);
border: 2px solid var(--sk-ink);
border-radius: 12px 255px 18px 225px / 225px 18px 255px 12px;
}
[data-theme="sketch"] .mantine-Input-input:focus,
[data-theme="sketch"] .mantine-Input-input:focus-within {
border-color: var(--sk-accent-ink);
}
[data-theme="sketch"] .mantine-InputWrapper-label {
font-family: var(--mantine-font-family-headings);
font-size: 1.15rem;
}
[data-theme="sketch"] .mantine-PinInput-input {
border-radius: 190px 16px 210px 14px / 14px 210px 16px 190px;
}
[data-theme="sketch"] .mantine-Checkbox-input {
background-color: var(--sk-paper-raised);
border: 2px solid var(--sk-ink);
border-radius: 155px 8px 145px 10px / 10px 145px 8px 155px;
}
[data-theme="sketch"] .mantine-Checkbox-icon {
color: var(--sk-on-fill);
}
[data-theme="sketch"] .mantine-Radio-radio {
background-color: var(--sk-paper-raised);
border: 2px solid var(--sk-ink);
border-radius: 48% 52% 51% 49% / 52% 48% 52% 48%;
}
[data-theme="sketch"] .mantine-Radio-icon {
color: var(--sk-on-fill);
}
[data-theme="sketch"] .mantine-Switch-track {
background-color: var(--sk-paper-raised);
border: 2px solid var(--sk-ink);
border-radius: 120px 40px 110px 45px / 45px 110px 40px 120px;
}
[data-theme="sketch"] .mantine-Switch-thumb {
background-color: var(--sk-paper-raised);
border: 2px solid var(--sk-ink);
border-radius: 50% 46% 54% 48% / 48% 54% 46% 52%;
}
/* ---------------------------------------------------------------- */
/* 8. Tabs — scribbled underline on the active tab */
/* ---------------------------------------------------------------- */
[data-theme="sketch"] .mantine-Tabs-tab {
border-radius: 200px 12px 190px 14px / 14px 190px 12px 200px;
font-weight: 400;
}
[data-theme="sketch"] .mantine-Tabs-tab[data-active] {
border-color: transparent;
background-image: var(--sk-squiggle);
background-repeat: repeat-x;
background-position: left bottom 1px;
background-size: 22px 5px;
color: var(--mantine-color-text);
}
[data-theme="sketch"] .mantine-SegmentedControl-root {
background-color: transparent;
border: 2px solid var(--sk-ink);
border-radius: 225px 18px 255px 15px / 15px 255px 18px 225px;
}
[data-theme="sketch"] .mantine-SegmentedControl-indicator {
background-color: var(--sk-wash);
border: 2px solid var(--sk-ink-soft);
border-radius: 205px 15px 235px 12px / 12px 235px 15px 205px;
box-shadow: none;
}
[data-theme="sketch"] .mantine-NavLink-root {
border: 2px solid transparent;
border-radius: 235px 18px 215px 20px / 20px 215px 18px 235px;
}
[data-theme="sketch"] .mantine-NavLink-root[data-active] {
border-color: var(--sk-ink-soft);
}
/* ---------------------------------------------------------------- */
/* 9. Small pieces */
/* ---------------------------------------------------------------- */
[data-theme="sketch"] .mantine-Badge-root {
border: 2px solid var(--sk-ink);
border-radius: 255px 10px 225px 12px / 12px 225px 10px 255px;
font-weight: 400;
letter-spacing: 0.02em;
}
[data-theme="sketch"] .mantine-ThemeIcon-root {
border: 2px solid var(--sk-ink);
border-radius: 170px 30px 150px 35px / 35px 150px 30px 170px;
}
[data-theme="sketch"] .mantine-Indicator-indicator {
border: 1.5px solid var(--sk-ink);
border-radius: 50% 45% 55% 48% / 52% 50% 46% 54%;
}
[data-theme="sketch"] .mantine-Skeleton-root {
border-radius: 225px 20px 245px 18px / 18px 245px 20px 225px;
}
[data-theme="sketch"] .mantine-Skeleton-root::after {
background-color: var(--sk-skeleton);
}
[data-theme="sketch"] .mantine-Loader-root {
--loader-color: var(--sk-accent-ink);
}
/* progress bar: wobbly pill with pencil cross-hatch fill */
[data-theme="sketch"] .mantine-Progress-root {
background-color: var(--sk-paper-raised);
border: 2px solid var(--sk-ink);
border-radius: 100px 30px 90px 35px / 35px 90px 30px 100px;
}
[data-theme="sketch"] .mantine-Progress-section {
background-image: repeating-linear-gradient(45deg, transparent 0 4px, var(--sk-hatch) 4px 6px);
}
[data-theme="sketch"] .mantine-Progress-label {
color: var(--sk-on-fill);
}
/* ---------------------------------------------------------------- */
/* 9b. App component hooks */
/* ---------------------------------------------------------------- */
/* Tab strip: flat on the page when resting; the raised paper strip with
its pencil rule appears only while docked. Resting keeps the 2px dashed
border transparent so docking doesn't shift layout. */
[data-theme="sketch"] .flxn-tabs {
border-bottom: 2px dashed transparent;
}
[data-theme="sketch"] .flxn-tabs[data-stuck] {
background-color: var(--sk-paper-raised);
border-bottom-color: var(--sk-ink-soft);
}
[data-theme="sketch"] .flxn-tab.flxn-tab {
position: relative;
border-radius: 200px 12px 190px 14px / 14px 190px 12px 200px;
}
/* active tab: accent watercolor wash + accent scribble underline */
[data-theme="sketch"] .flxn-tab.flxn-tab[data-active] {
background-color: var(--sk-wash);
color: var(--sk-accent-ink);
font-weight: 400; /* Patrick Hand has no bold */
}
[data-theme="sketch"] .flxn-tab.flxn-tab[data-active]::after {
content: "";
position: absolute;
inset-inline: 0.6rem;
bottom: 2px;
height: 5px;
background-color: var(--sk-accent-ink);
-webkit-mask-image: var(--sk-squiggle);
mask-image: var(--sk-squiggle);
-webkit-mask-repeat: repeat-x;
mask-repeat: repeat-x;
-webkit-mask-size: 22px 5px;
mask-size: 22px 5px;
}
/* the tab treatment carries the state — drop the straight underline */
[data-theme="sketch"] .flxn-tabs-indicator.flxn-tabs-indicator {
border-bottom: none;
}
/* Nav grid tiles: sketched boxes, each drawn slightly differently */
[data-theme="sketch"] .flxn-tile {
background-color: var(--sk-paper-raised);
border: 2px solid var(--sk-ink);
border-radius: 255px 15px 225px 18px / 18px 225px 15px 255px;
box-shadow: none;
}
[data-theme="sketch"] .flxn-tile:nth-child(even) {
border-radius: 15px 235px 18px 245px / 235px 15px 245px 18px;
}
[data-theme="sketch"] .flxn-tile:nth-child(3n) {
border-radius: 225px 22px 245px 15px / 15px 245px 22px 225px;
}
[data-theme="sketch"] .flxn-tile-icon {
color: var(--sk-accent-ink);
}
/* "See all" chip: hand-drawn pill; wash bg/hover come from the retuned
primary variant vars in hooks.css */
[data-theme="sketch"] .flxn-see-all.flxn-see-all {
border: 2px solid var(--sk-ink);
border-radius: 255px 12px 225px 14px / 14px 225px 12px 255px;
font-weight: 400;
}
/* Match card + reaction tray: one construction, matching 2px pen border */
[data-theme="sketch"] .flxn-match-card.flxn-match-card {
background-color: var(--sk-paper-raised);
border: 2px solid var(--sk-ink);
border-radius: 18px 235px 16px 245px / 235px 18px 245px 16px;
box-shadow: none;
}
[data-theme="sketch"] .flxn-reaction-row.flxn-reaction-row {
background-color: var(--sk-paper);
border: 2px solid var(--sk-ink);
border-radius: 15px 225px 18px 235px / 225px 15px 235px 18px;
}
/* ---------------------------------------------------------------- */
/* 10. Watercolor washes (light) / dusty chalk pastels (dark) */
/* Every palette color's variant vars are re-derived from its own */
/* numbered shades, so the user's accent (and any colored variant) */
/* shows through as a low-saturation wash instead of a solid fill. */
/* ---------------------------------------------------------------- */
[data-theme="sketch"][data-theme][data-mantine-color-scheme="light"] {
--mantine-color-red-filled: color-mix(in srgb, var(--mantine-color-red-6) 45%, #fffdf6);
--mantine-color-red-filled-hover: color-mix(in srgb, var(--mantine-color-red-6) 58%, #fffdf6);
--mantine-color-red-light: color-mix(in srgb, var(--mantine-color-red-6) 15%, #fffdf6);
--mantine-color-red-light-hover: color-mix(in srgb, var(--mantine-color-red-6) 23%, #fffdf6);
--mantine-color-red-light-color: color-mix(in srgb, var(--mantine-color-red-9) 72%, #333333);
--mantine-color-red-outline: color-mix(in srgb, var(--mantine-color-red-7) 65%, #333333);
--mantine-color-red-outline-hover: color-mix(in srgb, var(--mantine-color-red-6) 15%, #fffdf6);
--mantine-color-red-text: color-mix(in srgb, var(--mantine-color-red-8) 75%, #333333);
--mantine-color-pink-filled: color-mix(in srgb, var(--mantine-color-pink-6) 45%, #fffdf6);
--mantine-color-pink-filled-hover: color-mix(in srgb, var(--mantine-color-pink-6) 58%, #fffdf6);
--mantine-color-pink-light: color-mix(in srgb, var(--mantine-color-pink-6) 15%, #fffdf6);
--mantine-color-pink-light-hover: color-mix(in srgb, var(--mantine-color-pink-6) 23%, #fffdf6);
--mantine-color-pink-light-color: color-mix(in srgb, var(--mantine-color-pink-9) 72%, #333333);
--mantine-color-pink-outline: color-mix(in srgb, var(--mantine-color-pink-7) 65%, #333333);
--mantine-color-pink-outline-hover: color-mix(in srgb, var(--mantine-color-pink-6) 15%, #fffdf6);
--mantine-color-pink-text: color-mix(in srgb, var(--mantine-color-pink-8) 75%, #333333);
--mantine-color-grape-filled: color-mix(in srgb, var(--mantine-color-grape-6) 45%, #fffdf6);
--mantine-color-grape-filled-hover: color-mix(in srgb, var(--mantine-color-grape-6) 58%, #fffdf6);
--mantine-color-grape-light: color-mix(in srgb, var(--mantine-color-grape-6) 15%, #fffdf6);
--mantine-color-grape-light-hover: color-mix(in srgb, var(--mantine-color-grape-6) 23%, #fffdf6);
--mantine-color-grape-light-color: color-mix(in srgb, var(--mantine-color-grape-9) 72%, #333333);
--mantine-color-grape-outline: color-mix(in srgb, var(--mantine-color-grape-7) 65%, #333333);
--mantine-color-grape-outline-hover: color-mix(in srgb, var(--mantine-color-grape-6) 15%, #fffdf6);
--mantine-color-grape-text: color-mix(in srgb, var(--mantine-color-grape-8) 75%, #333333);
--mantine-color-violet-filled: color-mix(in srgb, var(--mantine-color-violet-6) 45%, #fffdf6);
--mantine-color-violet-filled-hover: color-mix(in srgb, var(--mantine-color-violet-6) 58%, #fffdf6);
--mantine-color-violet-light: color-mix(in srgb, var(--mantine-color-violet-6) 15%, #fffdf6);
--mantine-color-violet-light-hover: color-mix(in srgb, var(--mantine-color-violet-6) 23%, #fffdf6);
--mantine-color-violet-light-color: color-mix(in srgb, var(--mantine-color-violet-9) 72%, #333333);
--mantine-color-violet-outline: color-mix(in srgb, var(--mantine-color-violet-7) 65%, #333333);
--mantine-color-violet-outline-hover: color-mix(in srgb, var(--mantine-color-violet-6) 15%, #fffdf6);
--mantine-color-violet-text: color-mix(in srgb, var(--mantine-color-violet-8) 75%, #333333);
--mantine-color-indigo-filled: color-mix(in srgb, var(--mantine-color-indigo-6) 45%, #fffdf6);
--mantine-color-indigo-filled-hover: color-mix(in srgb, var(--mantine-color-indigo-6) 58%, #fffdf6);
--mantine-color-indigo-light: color-mix(in srgb, var(--mantine-color-indigo-6) 15%, #fffdf6);
--mantine-color-indigo-light-hover: color-mix(in srgb, var(--mantine-color-indigo-6) 23%, #fffdf6);
--mantine-color-indigo-light-color: color-mix(in srgb, var(--mantine-color-indigo-9) 72%, #333333);
--mantine-color-indigo-outline: color-mix(in srgb, var(--mantine-color-indigo-7) 65%, #333333);
--mantine-color-indigo-outline-hover: color-mix(in srgb, var(--mantine-color-indigo-6) 15%, #fffdf6);
--mantine-color-indigo-text: color-mix(in srgb, var(--mantine-color-indigo-8) 75%, #333333);
--mantine-color-blue-filled: color-mix(in srgb, var(--mantine-color-blue-6) 45%, #fffdf6);
--mantine-color-blue-filled-hover: color-mix(in srgb, var(--mantine-color-blue-6) 58%, #fffdf6);
--mantine-color-blue-light: color-mix(in srgb, var(--mantine-color-blue-6) 15%, #fffdf6);
--mantine-color-blue-light-hover: color-mix(in srgb, var(--mantine-color-blue-6) 23%, #fffdf6);
--mantine-color-blue-light-color: color-mix(in srgb, var(--mantine-color-blue-9) 72%, #333333);
--mantine-color-blue-outline: color-mix(in srgb, var(--mantine-color-blue-7) 65%, #333333);
--mantine-color-blue-outline-hover: color-mix(in srgb, var(--mantine-color-blue-6) 15%, #fffdf6);
--mantine-color-blue-text: color-mix(in srgb, var(--mantine-color-blue-8) 75%, #333333);
--mantine-color-cyan-filled: color-mix(in srgb, var(--mantine-color-cyan-6) 45%, #fffdf6);
--mantine-color-cyan-filled-hover: color-mix(in srgb, var(--mantine-color-cyan-6) 58%, #fffdf6);
--mantine-color-cyan-light: color-mix(in srgb, var(--mantine-color-cyan-6) 15%, #fffdf6);
--mantine-color-cyan-light-hover: color-mix(in srgb, var(--mantine-color-cyan-6) 23%, #fffdf6);
--mantine-color-cyan-light-color: color-mix(in srgb, var(--mantine-color-cyan-9) 72%, #333333);
--mantine-color-cyan-outline: color-mix(in srgb, var(--mantine-color-cyan-7) 65%, #333333);
--mantine-color-cyan-outline-hover: color-mix(in srgb, var(--mantine-color-cyan-6) 15%, #fffdf6);
--mantine-color-cyan-text: color-mix(in srgb, var(--mantine-color-cyan-8) 75%, #333333);
--mantine-color-teal-filled: color-mix(in srgb, var(--mantine-color-teal-6) 45%, #fffdf6);
--mantine-color-teal-filled-hover: color-mix(in srgb, var(--mantine-color-teal-6) 58%, #fffdf6);
--mantine-color-teal-light: color-mix(in srgb, var(--mantine-color-teal-6) 15%, #fffdf6);
--mantine-color-teal-light-hover: color-mix(in srgb, var(--mantine-color-teal-6) 23%, #fffdf6);
--mantine-color-teal-light-color: color-mix(in srgb, var(--mantine-color-teal-9) 72%, #333333);
--mantine-color-teal-outline: color-mix(in srgb, var(--mantine-color-teal-7) 65%, #333333);
--mantine-color-teal-outline-hover: color-mix(in srgb, var(--mantine-color-teal-6) 15%, #fffdf6);
--mantine-color-teal-text: color-mix(in srgb, var(--mantine-color-teal-8) 75%, #333333);
--mantine-color-green-filled: color-mix(in srgb, var(--mantine-color-green-6) 45%, #fffdf6);
--mantine-color-green-filled-hover: color-mix(in srgb, var(--mantine-color-green-6) 58%, #fffdf6);
--mantine-color-green-light: color-mix(in srgb, var(--mantine-color-green-6) 15%, #fffdf6);
--mantine-color-green-light-hover: color-mix(in srgb, var(--mantine-color-green-6) 23%, #fffdf6);
--mantine-color-green-light-color: color-mix(in srgb, var(--mantine-color-green-9) 72%, #333333);
--mantine-color-green-outline: color-mix(in srgb, var(--mantine-color-green-7) 65%, #333333);
--mantine-color-green-outline-hover: color-mix(in srgb, var(--mantine-color-green-6) 15%, #fffdf6);
--mantine-color-green-text: color-mix(in srgb, var(--mantine-color-green-8) 75%, #333333);
--mantine-color-lime-filled: color-mix(in srgb, var(--mantine-color-lime-6) 45%, #fffdf6);
--mantine-color-lime-filled-hover: color-mix(in srgb, var(--mantine-color-lime-6) 58%, #fffdf6);
--mantine-color-lime-light: color-mix(in srgb, var(--mantine-color-lime-6) 15%, #fffdf6);
--mantine-color-lime-light-hover: color-mix(in srgb, var(--mantine-color-lime-6) 23%, #fffdf6);
--mantine-color-lime-light-color: color-mix(in srgb, var(--mantine-color-lime-9) 72%, #333333);
--mantine-color-lime-outline: color-mix(in srgb, var(--mantine-color-lime-7) 65%, #333333);
--mantine-color-lime-outline-hover: color-mix(in srgb, var(--mantine-color-lime-6) 15%, #fffdf6);
--mantine-color-lime-text: color-mix(in srgb, var(--mantine-color-lime-8) 75%, #333333);
--mantine-color-yellow-filled: color-mix(in srgb, var(--mantine-color-yellow-6) 45%, #fffdf6);
--mantine-color-yellow-filled-hover: color-mix(in srgb, var(--mantine-color-yellow-6) 58%, #fffdf6);
--mantine-color-yellow-light: color-mix(in srgb, var(--mantine-color-yellow-6) 15%, #fffdf6);
--mantine-color-yellow-light-hover: color-mix(in srgb, var(--mantine-color-yellow-6) 23%, #fffdf6);
--mantine-color-yellow-light-color: color-mix(in srgb, var(--mantine-color-yellow-9) 72%, #333333);
--mantine-color-yellow-outline: color-mix(in srgb, var(--mantine-color-yellow-7) 65%, #333333);
--mantine-color-yellow-outline-hover: color-mix(in srgb, var(--mantine-color-yellow-6) 15%, #fffdf6);
--mantine-color-yellow-text: color-mix(in srgb, var(--mantine-color-yellow-8) 75%, #333333);
--mantine-color-orange-filled: color-mix(in srgb, var(--mantine-color-orange-6) 45%, #fffdf6);
--mantine-color-orange-filled-hover: color-mix(in srgb, var(--mantine-color-orange-6) 58%, #fffdf6);
--mantine-color-orange-light: color-mix(in srgb, var(--mantine-color-orange-6) 15%, #fffdf6);
--mantine-color-orange-light-hover: color-mix(in srgb, var(--mantine-color-orange-6) 23%, #fffdf6);
--mantine-color-orange-light-color: color-mix(in srgb, var(--mantine-color-orange-9) 72%, #333333);
--mantine-color-orange-outline: color-mix(in srgb, var(--mantine-color-orange-7) 65%, #333333);
--mantine-color-orange-outline-hover: color-mix(in srgb, var(--mantine-color-orange-6) 15%, #fffdf6);
--mantine-color-orange-text: color-mix(in srgb, var(--mantine-color-orange-8) 75%, #333333);
--mantine-color-gray-filled: color-mix(in srgb, var(--mantine-color-gray-6) 45%, #fffdf6);
--mantine-color-gray-filled-hover: color-mix(in srgb, var(--mantine-color-gray-6) 58%, #fffdf6);
--mantine-color-gray-light: color-mix(in srgb, var(--mantine-color-gray-6) 15%, #fffdf6);
--mantine-color-gray-light-hover: color-mix(in srgb, var(--mantine-color-gray-6) 23%, #fffdf6);
--mantine-color-gray-light-color: color-mix(in srgb, var(--mantine-color-gray-9) 72%, #333333);
--mantine-color-gray-outline: color-mix(in srgb, var(--mantine-color-gray-7) 65%, #333333);
--mantine-color-gray-outline-hover: color-mix(in srgb, var(--mantine-color-gray-6) 15%, #fffdf6);
--mantine-color-gray-text: color-mix(in srgb, var(--mantine-color-gray-8) 75%, #333333);
}
[data-theme="sketch"][data-theme][data-mantine-color-scheme="dark"] {
--mantine-color-red-filled: color-mix(in srgb, var(--mantine-color-red-3) 62%, #d9d5c7);
--mantine-color-red-filled-hover: color-mix(in srgb, var(--mantine-color-red-3) 76%, #d9d5c7);
--mantine-color-red-light: color-mix(in srgb, var(--mantine-color-red-4) 20%, #2a322d);
--mantine-color-red-light-hover: color-mix(in srgb, var(--mantine-color-red-4) 30%, #2a322d);
--mantine-color-red-light-color: color-mix(in srgb, var(--mantine-color-red-2) 78%, #e8e5da);
--mantine-color-red-outline: color-mix(in srgb, var(--mantine-color-red-2) 80%, #dfdcd0);
--mantine-color-red-outline-hover: color-mix(in srgb, var(--mantine-color-red-4) 22%, #2a322d);
--mantine-color-red-text: color-mix(in srgb, var(--mantine-color-red-2) 80%, #e8e5da);
--mantine-color-pink-filled: color-mix(in srgb, var(--mantine-color-pink-3) 62%, #d9d5c7);
--mantine-color-pink-filled-hover: color-mix(in srgb, var(--mantine-color-pink-3) 76%, #d9d5c7);
--mantine-color-pink-light: color-mix(in srgb, var(--mantine-color-pink-4) 20%, #2a322d);
--mantine-color-pink-light-hover: color-mix(in srgb, var(--mantine-color-pink-4) 30%, #2a322d);
--mantine-color-pink-light-color: color-mix(in srgb, var(--mantine-color-pink-2) 78%, #e8e5da);
--mantine-color-pink-outline: color-mix(in srgb, var(--mantine-color-pink-2) 80%, #dfdcd0);
--mantine-color-pink-outline-hover: color-mix(in srgb, var(--mantine-color-pink-4) 22%, #2a322d);
--mantine-color-pink-text: color-mix(in srgb, var(--mantine-color-pink-2) 80%, #e8e5da);
--mantine-color-grape-filled: color-mix(in srgb, var(--mantine-color-grape-3) 62%, #d9d5c7);
--mantine-color-grape-filled-hover: color-mix(in srgb, var(--mantine-color-grape-3) 76%, #d9d5c7);
--mantine-color-grape-light: color-mix(in srgb, var(--mantine-color-grape-4) 20%, #2a322d);
--mantine-color-grape-light-hover: color-mix(in srgb, var(--mantine-color-grape-4) 30%, #2a322d);
--mantine-color-grape-light-color: color-mix(in srgb, var(--mantine-color-grape-2) 78%, #e8e5da);
--mantine-color-grape-outline: color-mix(in srgb, var(--mantine-color-grape-2) 80%, #dfdcd0);
--mantine-color-grape-outline-hover: color-mix(in srgb, var(--mantine-color-grape-4) 22%, #2a322d);
--mantine-color-grape-text: color-mix(in srgb, var(--mantine-color-grape-2) 80%, #e8e5da);
--mantine-color-violet-filled: color-mix(in srgb, var(--mantine-color-violet-3) 62%, #d9d5c7);
--mantine-color-violet-filled-hover: color-mix(in srgb, var(--mantine-color-violet-3) 76%, #d9d5c7);
--mantine-color-violet-light: color-mix(in srgb, var(--mantine-color-violet-4) 20%, #2a322d);
--mantine-color-violet-light-hover: color-mix(in srgb, var(--mantine-color-violet-4) 30%, #2a322d);
--mantine-color-violet-light-color: color-mix(in srgb, var(--mantine-color-violet-2) 78%, #e8e5da);
--mantine-color-violet-outline: color-mix(in srgb, var(--mantine-color-violet-2) 80%, #dfdcd0);
--mantine-color-violet-outline-hover: color-mix(in srgb, var(--mantine-color-violet-4) 22%, #2a322d);
--mantine-color-violet-text: color-mix(in srgb, var(--mantine-color-violet-2) 80%, #e8e5da);
--mantine-color-indigo-filled: color-mix(in srgb, var(--mantine-color-indigo-3) 62%, #d9d5c7);
--mantine-color-indigo-filled-hover: color-mix(in srgb, var(--mantine-color-indigo-3) 76%, #d9d5c7);
--mantine-color-indigo-light: color-mix(in srgb, var(--mantine-color-indigo-4) 20%, #2a322d);
--mantine-color-indigo-light-hover: color-mix(in srgb, var(--mantine-color-indigo-4) 30%, #2a322d);
--mantine-color-indigo-light-color: color-mix(in srgb, var(--mantine-color-indigo-2) 78%, #e8e5da);
--mantine-color-indigo-outline: color-mix(in srgb, var(--mantine-color-indigo-2) 80%, #dfdcd0);
--mantine-color-indigo-outline-hover: color-mix(in srgb, var(--mantine-color-indigo-4) 22%, #2a322d);
--mantine-color-indigo-text: color-mix(in srgb, var(--mantine-color-indigo-2) 80%, #e8e5da);
--mantine-color-blue-filled: color-mix(in srgb, var(--mantine-color-blue-3) 62%, #d9d5c7);
--mantine-color-blue-filled-hover: color-mix(in srgb, var(--mantine-color-blue-3) 76%, #d9d5c7);
--mantine-color-blue-light: color-mix(in srgb, var(--mantine-color-blue-4) 20%, #2a322d);
--mantine-color-blue-light-hover: color-mix(in srgb, var(--mantine-color-blue-4) 30%, #2a322d);
--mantine-color-blue-light-color: color-mix(in srgb, var(--mantine-color-blue-2) 78%, #e8e5da);
--mantine-color-blue-outline: color-mix(in srgb, var(--mantine-color-blue-2) 80%, #dfdcd0);
--mantine-color-blue-outline-hover: color-mix(in srgb, var(--mantine-color-blue-4) 22%, #2a322d);
--mantine-color-blue-text: color-mix(in srgb, var(--mantine-color-blue-2) 80%, #e8e5da);
--mantine-color-cyan-filled: color-mix(in srgb, var(--mantine-color-cyan-3) 62%, #d9d5c7);
--mantine-color-cyan-filled-hover: color-mix(in srgb, var(--mantine-color-cyan-3) 76%, #d9d5c7);
--mantine-color-cyan-light: color-mix(in srgb, var(--mantine-color-cyan-4) 20%, #2a322d);
--mantine-color-cyan-light-hover: color-mix(in srgb, var(--mantine-color-cyan-4) 30%, #2a322d);
--mantine-color-cyan-light-color: color-mix(in srgb, var(--mantine-color-cyan-2) 78%, #e8e5da);
--mantine-color-cyan-outline: color-mix(in srgb, var(--mantine-color-cyan-2) 80%, #dfdcd0);
--mantine-color-cyan-outline-hover: color-mix(in srgb, var(--mantine-color-cyan-4) 22%, #2a322d);
--mantine-color-cyan-text: color-mix(in srgb, var(--mantine-color-cyan-2) 80%, #e8e5da);
--mantine-color-teal-filled: color-mix(in srgb, var(--mantine-color-teal-3) 62%, #d9d5c7);
--mantine-color-teal-filled-hover: color-mix(in srgb, var(--mantine-color-teal-3) 76%, #d9d5c7);
--mantine-color-teal-light: color-mix(in srgb, var(--mantine-color-teal-4) 20%, #2a322d);
--mantine-color-teal-light-hover: color-mix(in srgb, var(--mantine-color-teal-4) 30%, #2a322d);
--mantine-color-teal-light-color: color-mix(in srgb, var(--mantine-color-teal-2) 78%, #e8e5da);
--mantine-color-teal-outline: color-mix(in srgb, var(--mantine-color-teal-2) 80%, #dfdcd0);
--mantine-color-teal-outline-hover: color-mix(in srgb, var(--mantine-color-teal-4) 22%, #2a322d);
--mantine-color-teal-text: color-mix(in srgb, var(--mantine-color-teal-2) 80%, #e8e5da);
--mantine-color-green-filled: color-mix(in srgb, var(--mantine-color-green-3) 62%, #d9d5c7);
--mantine-color-green-filled-hover: color-mix(in srgb, var(--mantine-color-green-3) 76%, #d9d5c7);
--mantine-color-green-light: color-mix(in srgb, var(--mantine-color-green-4) 20%, #2a322d);
--mantine-color-green-light-hover: color-mix(in srgb, var(--mantine-color-green-4) 30%, #2a322d);
--mantine-color-green-light-color: color-mix(in srgb, var(--mantine-color-green-2) 78%, #e8e5da);
--mantine-color-green-outline: color-mix(in srgb, var(--mantine-color-green-2) 80%, #dfdcd0);
--mantine-color-green-outline-hover: color-mix(in srgb, var(--mantine-color-green-4) 22%, #2a322d);
--mantine-color-green-text: color-mix(in srgb, var(--mantine-color-green-2) 80%, #e8e5da);
--mantine-color-lime-filled: color-mix(in srgb, var(--mantine-color-lime-3) 62%, #d9d5c7);
--mantine-color-lime-filled-hover: color-mix(in srgb, var(--mantine-color-lime-3) 76%, #d9d5c7);
--mantine-color-lime-light: color-mix(in srgb, var(--mantine-color-lime-4) 20%, #2a322d);
--mantine-color-lime-light-hover: color-mix(in srgb, var(--mantine-color-lime-4) 30%, #2a322d);
--mantine-color-lime-light-color: color-mix(in srgb, var(--mantine-color-lime-2) 78%, #e8e5da);
--mantine-color-lime-outline: color-mix(in srgb, var(--mantine-color-lime-2) 80%, #dfdcd0);
--mantine-color-lime-outline-hover: color-mix(in srgb, var(--mantine-color-lime-4) 22%, #2a322d);
--mantine-color-lime-text: color-mix(in srgb, var(--mantine-color-lime-2) 80%, #e8e5da);
--mantine-color-yellow-filled: color-mix(in srgb, var(--mantine-color-yellow-3) 62%, #d9d5c7);
--mantine-color-yellow-filled-hover: color-mix(in srgb, var(--mantine-color-yellow-3) 76%, #d9d5c7);
--mantine-color-yellow-light: color-mix(in srgb, var(--mantine-color-yellow-4) 20%, #2a322d);
--mantine-color-yellow-light-hover: color-mix(in srgb, var(--mantine-color-yellow-4) 30%, #2a322d);
--mantine-color-yellow-light-color: color-mix(in srgb, var(--mantine-color-yellow-2) 78%, #e8e5da);
--mantine-color-yellow-outline: color-mix(in srgb, var(--mantine-color-yellow-2) 80%, #dfdcd0);
--mantine-color-yellow-outline-hover: color-mix(in srgb, var(--mantine-color-yellow-4) 22%, #2a322d);
--mantine-color-yellow-text: color-mix(in srgb, var(--mantine-color-yellow-2) 80%, #e8e5da);
--mantine-color-orange-filled: color-mix(in srgb, var(--mantine-color-orange-3) 62%, #d9d5c7);
--mantine-color-orange-filled-hover: color-mix(in srgb, var(--mantine-color-orange-3) 76%, #d9d5c7);
--mantine-color-orange-light: color-mix(in srgb, var(--mantine-color-orange-4) 20%, #2a322d);
--mantine-color-orange-light-hover: color-mix(in srgb, var(--mantine-color-orange-4) 30%, #2a322d);
--mantine-color-orange-light-color: color-mix(in srgb, var(--mantine-color-orange-2) 78%, #e8e5da);
--mantine-color-orange-outline: color-mix(in srgb, var(--mantine-color-orange-2) 80%, #dfdcd0);
--mantine-color-orange-outline-hover: color-mix(in srgb, var(--mantine-color-orange-4) 22%, #2a322d);
--mantine-color-orange-text: color-mix(in srgb, var(--mantine-color-orange-2) 80%, #e8e5da);
--mantine-color-gray-filled: color-mix(in srgb, var(--mantine-color-gray-3) 62%, #d9d5c7);
--mantine-color-gray-filled-hover: color-mix(in srgb, var(--mantine-color-gray-3) 76%, #d9d5c7);
--mantine-color-gray-light: color-mix(in srgb, var(--mantine-color-gray-4) 20%, #2a322d);
--mantine-color-gray-light-hover: color-mix(in srgb, var(--mantine-color-gray-4) 30%, #2a322d);
--mantine-color-gray-light-color: color-mix(in srgb, var(--mantine-color-gray-2) 78%, #e8e5da);
--mantine-color-gray-outline: color-mix(in srgb, var(--mantine-color-gray-2) 80%, #dfdcd0);
--mantine-color-gray-outline-hover: color-mix(in srgb, var(--mantine-color-gray-4) 22%, #2a322d);
--mantine-color-gray-text: color-mix(in srgb, var(--mantine-color-gray-2) 80%, #e8e5da);
}
+17
View File
@@ -0,0 +1,17 @@
import { msg } from "@lingui/core/macro";
import type { ThemeManifest } from "../types";
const manifest: ThemeManifest = {
id: "skeuo",
label: msg`Skeuomorphic`,
colorSchemes: "both",
fontLinks: [
"https://fonts.googleapis.com/css2?family=Lato:wght@400;700;900&display=swap",
],
chromeColors: {
light: { base: "#b7bdc5", dimmed: "#8b929b" },
dark: { base: "#2a2d31", dimmed: "#141619" },
},
};
export default manifest;
+947
View File
@@ -0,0 +1,947 @@
/* Skeuomorphic — 2010 machined realism (iOS 4 / brushed aluminum era).
Light: warm linen body, machined-pill controls, deeply inset inputs.
Dark: graphite/leather with subtle white inset highlights.
All textures are inline data-URIs; real 1px borders everywhere so
forced-colors mode keeps usable outlines. */
/* ---------- 1. Tokens, any scheme ---------- */
[data-theme="skeuo"][data-theme] {
--mantine-font-family: "Lato", "Inter", "Helvetica Neue", Helvetica, sans-serif;
--mantine-font-family-headings: "Lato", "Inter", "Helvetica Neue", Helvetica, sans-serif;
--mantine-heading-font-weight: 900;
--mantine-radius-xs: 0.25rem;
--mantine-radius-sm: 0.375rem;
--mantine-radius-md: 0.5rem;
--mantine-radius-lg: 0.75rem;
--mantine-radius-xl: 1rem;
--mantine-radius-default: 0.5rem;
--flxn-avatar-radius: 50%;
--flxn-avatar-border: 1px solid rgba(0, 0, 0, 0.3);
--flxn-badge-radius: 10px;
/* anodized accent ramps — interactive shades 5-9 deepened and slightly
desaturated so filled surfaces read as lacquered metal in any hue */
--mantine-color-blue-5: #3a8ad3;
--mantine-color-blue-6: #1f78c8;
--mantine-color-blue-7: #1a67ad;
--mantine-color-blue-8: #165890;
--mantine-color-blue-9: #124874;
--mantine-color-red-5: #e05c56;
--mantine-color-red-6: #d64545;
--mantine-color-red-7: #bb3a3a;
--mantine-color-red-8: #9e3131;
--mantine-color-red-9: #822928;
--mantine-color-green-5: #4bb35c;
--mantine-color-green-6: #379f4d;
--mantine-color-green-7: #2e8841;
--mantine-color-green-8: #267136;
--mantine-color-green-9: #1e5c2c;
--mantine-color-yellow-5: #ecad14;
--mantine-color-yellow-6: #d99a06;
--mantine-color-yellow-7: #bb8405;
--mantine-color-yellow-8: #9c6e04;
--mantine-color-yellow-9: #7f5a04;
--mantine-color-grape-5: #b355cb;
--mantine-color-grape-6: #a341bd;
--mantine-color-grape-7: #8d37a4;
--mantine-color-grape-8: #762e8a;
--mantine-color-grape-9: #612671;
--mantine-color-orange-5: #ef8221;
--mantine-color-orange-6: #dd6f13;
--mantine-color-orange-7: #c05f10;
--mantine-color-orange-8: #a2500d;
--mantine-color-orange-9: #86420b;
--mantine-color-pink-5: #d75a86;
--mantine-color-pink-6: #c8406f;
--mantine-color-pink-7: #ad365f;
--mantine-color-pink-8: #922d50;
--mantine-color-pink-9: #782542;
--mantine-color-lime-5: #83bb2f;
--mantine-color-lime-6: #71ab1d;
--mantine-color-lime-7: #619219;
--mantine-color-lime-8: #517a15;
--mantine-color-lime-9: #426311;
/* fractal-noise grain, tiled; ~4% opacity baked into the rect */
--skeuo-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.75' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.045'/%3E%3C/svg%3E");
/* fine horizontal brushing for metal surfaces */
--skeuo-brush: repeating-linear-gradient(
180deg,
rgba(255, 255, 255, 0.05) 0 1px,
rgba(0, 0, 0, 0.03) 1px 2px,
transparent 2px 3px
);
}
/* ---------- 2. Per-scheme tokens ---------- */
[data-theme="skeuo"][data-theme][data-mantine-color-scheme="light"] {
--mantine-color-body: #e8e4da;
--mantine-color-text: #3b362e;
--mantine-color-bright: #1f1b15;
--mantine-color-dimmed: #665e4e;
--mantine-color-placeholder: #7d745f;
--mantine-color-anchor: #3a6ea5;
--mantine-color-error: #b0382e;
--mantine-color-default: #eeebe2;
--mantine-color-default-hover: #e3dfd2;
--mantine-color-default-color: #3b362e;
--mantine-color-default-border: #a99f8c;
/* warm machined grays */
--mantine-color-gray-0: #faf8f3;
--mantine-color-gray-1: #f1eee6;
--mantine-color-gray-2: #e6e2d6;
--mantine-color-gray-3: #d7d2c3;
--mantine-color-gray-4: #c3bcaa;
--mantine-color-gray-5: #a99f8c;
--mantine-color-gray-6: #857c6c;
--mantine-color-gray-7: #5f584b;
--mantine-color-gray-8: #403a30;
--mantine-color-gray-9: #29241d;
/* raised-panel shadow stacks: inset top highlight + soft drop */
--mantine-shadow-xs: inset 0 1px 0 rgba(255, 255, 255, 0.65),
0 1px 2px rgba(46, 41, 33, 0.16);
--mantine-shadow-sm: inset 0 1px 0 rgba(255, 255, 255, 0.7),
0 1px 2px rgba(46, 41, 33, 0.18), 0 3px 8px rgba(46, 41, 33, 0.1);
--mantine-shadow-md: inset 0 1px 0 rgba(255, 255, 255, 0.75),
0 2px 4px rgba(46, 41, 33, 0.16), 0 7px 16px rgba(46, 41, 33, 0.13);
--mantine-shadow-lg: inset 0 1px 0 rgba(255, 255, 255, 0.75),
0 4px 8px rgba(46, 41, 33, 0.18), 0 14px 28px rgba(46, 41, 33, 0.16);
--mantine-shadow-xl: inset 0 1px 0 rgba(255, 255, 255, 0.8),
0 6px 12px rgba(46, 41, 33, 0.2), 0 22px 44px rgba(46, 41, 33, 0.2);
/* app tokens */
--flxn-gold: #cf9312;
--flxn-silver: #8f97a1;
--flxn-bronze: #b06f36;
--flxn-toast-success: #3d8f43;
--flxn-toast-error: #bf4136;
/* labels: engraved neutral inks — never the plate's own metal hue */
--flxn-podium-1-bg: linear-gradient(180deg, #fbecbb 0%, #efd489 48%, #e0b955 52%, #d8ad42 100%);
--flxn-podium-1-border: #b18a2b;
--flxn-podium-1-label: #46402e;
--flxn-podium-2-bg: linear-gradient(180deg, #f7f8fa 0%, #e2e5e9 48%, #ccd0d6 52%, #c2c7ce 100%);
--flxn-podium-2-border: #969da6;
--flxn-podium-2-label: #3f444b;
--flxn-podium-3-bg: linear-gradient(180deg, #f0d3b4 0%, #ddad7d 48%, #ca9257 52%, #c08a4e 100%);
--flxn-podium-3-border: #99672f;
--flxn-podium-3-label: #2f2822;
--flxn-showcase-bg: linear-gradient(180deg, #fbf9f4 0%, #efece2 100%);
--flxn-overlay: rgba(30, 26, 20, 0.45);
--flxn-dotgrid-color: #c9c2b1;
--flxn-pop-shadow: rgba(46, 41, 33, 0.35);
}
[data-theme="skeuo"][data-theme][data-mantine-color-scheme="dark"] {
--mantine-color-body: #1a1b1e;
--mantine-color-text: #d5d2ca;
--mantine-color-bright: #f2efe8;
--mantine-color-dimmed: #8f8c84;
--mantine-color-placeholder: #6f6c64;
--mantine-color-anchor: #82abdd;
--mantine-color-error: #e05e52;
--mantine-color-default: #2b2d31;
--mantine-color-default-hover: #34373c;
--mantine-color-default-color: #d5d2ca;
--mantine-color-default-border: #46484d;
/* graphite darks */
--mantine-color-dark-0: #e0ddd6;
--mantine-color-dark-1: #c7c4bd;
--mantine-color-dark-2: #94918a;
--mantine-color-dark-3: #4c4f55;
--mantine-color-dark-4: #3a3d42;
--mantine-color-dark-5: #2b2d31;
--mantine-color-dark-6: #222428;
--mantine-color-dark-7: #1a1b1e;
--mantine-color-dark-8: #141518;
--mantine-color-dark-9: #0e0f11;
--mantine-shadow-xs: inset 0 1px 0 rgba(255, 255, 255, 0.06),
0 1px 2px rgba(0, 0, 0, 0.5);
--mantine-shadow-sm: inset 0 1px 0 rgba(255, 255, 255, 0.07),
0 1px 3px rgba(0, 0, 0, 0.55), 0 3px 8px rgba(0, 0, 0, 0.35);
--mantine-shadow-md: inset 0 1px 0 rgba(255, 255, 255, 0.08),
0 2px 5px rgba(0, 0, 0, 0.55), 0 8px 18px rgba(0, 0, 0, 0.4);
--mantine-shadow-lg: inset 0 1px 0 rgba(255, 255, 255, 0.08),
0 4px 10px rgba(0, 0, 0, 0.6), 0 16px 32px rgba(0, 0, 0, 0.45);
--mantine-shadow-xl: inset 0 1px 0 rgba(255, 255, 255, 0.09),
0 6px 14px rgba(0, 0, 0, 0.65), 0 24px 48px rgba(0, 0, 0, 0.5);
--flxn-gold: #e7b93c;
--flxn-silver: #a9b0ba;
--flxn-bronze: #cd8a4c;
--flxn-toast-success: #6fca75;
--flxn-toast-error: #ef8078;
--flxn-podium-1-bg: linear-gradient(180deg, #4d3f1c 0%, #3a3016 48%, #2c2510 52%, #272009 100%);
--flxn-podium-1-border: #7c6425;
--flxn-podium-1-label: #ecc65a;
--flxn-podium-2-bg: linear-gradient(180deg, #3c3f45 0%, #2f3237 48%, #26282c 52%, #222428 100%);
--flxn-podium-2-border: #5c6069;
--flxn-podium-2-label: #bcc2cb;
--flxn-podium-3-bg: linear-gradient(180deg, #46311c 0%, #372613 48%, #2a1d0d 52%, #251a09 100%);
--flxn-podium-3-border: #7a5528;
--flxn-podium-3-label: #dc9c5c;
--flxn-showcase-bg: linear-gradient(180deg, #26282c 0%, #1d1f22 100%);
--flxn-overlay: rgba(0, 0, 0, 0.55);
--flxn-dotgrid-color: #33363b;
--flxn-pop-shadow: rgba(0, 0, 0, 0.6);
}
/* Light scheme only: Mantine pins --mantine-primary-color-contrast to white
(no JS autoContrast), unreadable on the light shade-6 fills (yellow,
orange, lime, green). Recompute from the fill's lightness — dark-scheme
shade-8 fills all carry white fine and keep the default. */
@supports (color: oklch(from red calc((0.5 - l) * infinity) 0 h)) {
[data-theme="skeuo"][data-theme][data-mantine-color-scheme="light"] {
--mantine-primary-color-contrast: oklch(
from var(--mantine-primary-color-filled) calc((0.59 - l) * infinity) 0 h
);
}
}
/* ---------- Body texture (linen / leather grain) ---------- */
[data-theme="skeuo"][data-mantine-color-scheme="light"] body {
background-color: var(--mantine-color-body);
background-image: var(--skeuo-noise),
repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.04) 0 1px, transparent 1px 3px),
repeating-linear-gradient(90deg, rgba(66, 56, 40, 0.025) 0 1px, transparent 1px 3px);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] body {
background-color: var(--mantine-color-body);
background-image: var(--skeuo-noise),
radial-gradient(130% 80% at 50% 0%, rgba(255, 255, 255, 0.035), transparent 60%);
}
/* ---------- Typography: embossed / debossed labels ---------- */
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-Title-root {
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-Title-root {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.7);
}
/* ---------- Buttons & ActionIcons: machined pills ---------- */
[data-theme="skeuo"] .mantine-Button-root,
[data-theme="skeuo"] .mantine-ActionIcon-root {
border: 1px solid var(--mantine-color-default-border);
border-radius: 100px;
}
/* default variant — bare aluminum */
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-Button-root[data-variant="default"],
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-ActionIcon-root[data-variant="default"] {
background-color: #e3dfd2;
background-image: linear-gradient(180deg, #fdfcf8 0%, #ece8dc 48%, #dcd7c8 52%, #d0cabb 100%);
border-color: #9d947f;
color: #3b362e;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.8);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9),
inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 2px rgba(46, 41, 33, 0.3);
}
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-Button-root[data-variant="default"]:hover,
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-ActionIcon-root[data-variant="default"]:hover {
background-image: linear-gradient(180deg, #fffefb 0%, #f2eee3 48%, #e3decf 52%, #d8d2c3 100%);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-Button-root[data-variant="default"],
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-ActionIcon-root[data-variant="default"] {
background-color: #2b2d31;
background-image: linear-gradient(180deg, #40434a 0%, #303338 48%, #26282c 52%, #202226 100%);
border-color: #0d0e10;
color: #d5d2ca;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.6);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12),
inset 0 -1px 0 rgba(0, 0, 0, 0.4), 0 1px 2px rgba(0, 0, 0, 0.55);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-Button-root[data-variant="default"]:hover,
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-ActionIcon-root[data-variant="default"]:hover {
background-image: linear-gradient(180deg, #474a52 0%, #363940 48%, #2b2d32 52%, #24262b 100%);
}
/* filled variant — glossy accent metal (keeps the user's accent) */
[data-theme="skeuo"] .mantine-Button-root[data-variant="filled"],
[data-theme="skeuo"] .mantine-ActionIcon-root[data-variant="filled"] {
background-color: var(--mantine-primary-color-filled);
background-image: linear-gradient(
180deg,
color-mix(in srgb, var(--mantine-primary-color-filled) 62%, white) 0%,
color-mix(in srgb, var(--mantine-primary-color-filled) 92%, white) 48%,
var(--mantine-primary-color-filled) 52%,
color-mix(in srgb, var(--mantine-primary-color-filled) 82%, black) 100%
);
border-color: color-mix(in srgb, var(--mantine-primary-color-filled) 55%, black);
color: var(--mantine-primary-color-contrast);
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.35);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45),
inset 0 -1px 0 rgba(0, 0, 0, 0.25), 0 1px 3px rgba(0, 0, 0, 0.35);
}
/* prop-less Button/ActionIcon default to the filled variant without a
data-variant attribute — give them the same on-color */
[data-theme="skeuo"] .mantine-Button-root:not([data-variant]),
[data-theme="skeuo"] .mantine-ActionIcon-root:not([data-variant]) {
color: var(--mantine-primary-color-contrast);
}
[data-theme="skeuo"] .mantine-Button-root[data-variant="filled"]:hover,
[data-theme="skeuo"] .mantine-ActionIcon-root[data-variant="filled"]:hover {
background-image: linear-gradient(
180deg,
color-mix(in srgb, var(--mantine-primary-color-filled) 52%, white) 0%,
color-mix(in srgb, var(--mantine-primary-color-filled) 84%, white) 48%,
var(--mantine-primary-color-filled) 52%,
color-mix(in srgb, var(--mantine-primary-color-filled) 78%, black) 100%
);
}
/* light / subtle / outline — etched into the surface */
[data-theme="skeuo"] .mantine-Button-root[data-variant="light"],
[data-theme="skeuo"] .mantine-ActionIcon-root[data-variant="light"] {
border-color: color-mix(in srgb, var(--mantine-primary-color-filled) 35%, transparent);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.08);
}
[data-theme="skeuo"] .mantine-Button-root[data-variant="outline"],
[data-theme="skeuo"] .mantine-ActionIcon-root[data-variant="outline"] {
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1),
inset 0 -1px 0 rgba(255, 255, 255, 0.15);
}
[data-theme="skeuo"] .mantine-Button-root[data-variant="subtle"],
[data-theme="skeuo"] .mantine-ActionIcon-root[data-variant="subtle"],
[data-theme="skeuo"] .mantine-Button-root[data-variant="transparent"],
[data-theme="skeuo"] .mantine-ActionIcon-root[data-variant="transparent"] {
border-color: transparent;
}
/* pressed — inverted gradient + sunk-in inset */
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-Button-root[data-variant="default"]:active,
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-ActionIcon-root[data-variant="default"]:active {
background-image: linear-gradient(180deg, #c8c2b1 0%, #d5d0c0 48%, #e4e0d3 52%, #efece2 100%);
box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.28),
inset 0 1px 2px rgba(0, 0, 0, 0.22), 0 1px 0 rgba(255, 255, 255, 0.6);
text-shadow: none;
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-Button-root[data-variant="default"]:active,
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-ActionIcon-root[data-variant="default"]:active {
background-image: linear-gradient(180deg, #17181b 0%, #202226 48%, #2b2d32 52%, #34363c 100%);
box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.65),
0 1px 0 rgba(255, 255, 255, 0.06);
text-shadow: none;
}
[data-theme="skeuo"] .mantine-Button-root[data-variant="filled"]:active,
[data-theme="skeuo"] .mantine-ActionIcon-root[data-variant="filled"]:active {
background-image: linear-gradient(
180deg,
color-mix(in srgb, var(--mantine-primary-color-filled) 75%, black) 0%,
var(--mantine-primary-color-filled) 52%,
color-mix(in srgb, var(--mantine-primary-color-filled) 85%, white) 100%
);
box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.4);
text-shadow: none;
}
[data-theme="skeuo"] .mantine-Button-root:disabled,
[data-theme="skeuo"] .mantine-Button-root[data-disabled],
[data-theme="skeuo"] .mantine-ActionIcon-root:disabled,
[data-theme="skeuo"] .mantine-ActionIcon-root[data-disabled] {
background-image: none;
box-shadow: none;
text-shadow: none;
border: 1px solid var(--mantine-color-default-border);
}
/* press feedback stays subtle — machined buttons sink, not shrink */
@media (prefers-reduced-motion: no-preference) {
[data-theme="skeuo"] .flxn-press:active {
transform: scale(0.98) translateY(1px);
}
}
/* ---------- Inputs: deeply inset wells ---------- */
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-Input-input {
background-color: #fbfaf5;
background-image: linear-gradient(180deg, #efece2 0%, #f8f6ef 18%, #fdfcf8 100%);
border: 1px solid #a1977f;
border-radius: var(--mantine-radius-md);
color: var(--mantine-color-text);
box-shadow: inset 0 1px 3px rgba(46, 41, 33, 0.24),
inset 0 2px 6px rgba(46, 41, 33, 0.08), 0 1px 0 rgba(255, 255, 255, 0.7);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-Input-input {
background-color: #121316;
background-image: linear-gradient(180deg, #0c0d0f 0%, #121316 25%, #17181c 100%);
border: 1px solid #0a0b0c;
border-radius: var(--mantine-radius-md);
color: var(--mantine-color-text);
box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.65),
inset 0 2px 8px rgba(0, 0, 0, 0.3), 0 1px 0 rgba(255, 255, 255, 0.07);
}
[data-theme="skeuo"] .mantine-Input-input:focus,
[data-theme="skeuo"] .mantine-Input-input:focus-within {
border-color: var(--mantine-primary-color-filled);
outline: none;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.25),
0 0 0 3px color-mix(in srgb, var(--mantine-primary-color-filled) 30%, transparent);
}
/* ---------- Panels: Paper, Card, dropdowns, Modal ---------- */
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-Paper-root,
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-Card-root,
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-Popover-dropdown,
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-Modal-content {
background-color: #f6f3ec;
background-image: linear-gradient(180deg, #fcfbf7 0%, #f1eee5 100%);
border: 1px solid #c2bbaa;
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-Paper-root,
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-Card-root,
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-Popover-dropdown,
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-Modal-content {
background-color: #222428;
background-image: linear-gradient(180deg, #27292e 0%, #1e2023 100%);
border: 1px solid #0d0e10;
}
[data-theme="skeuo"] .mantine-Paper-root,
[data-theme="skeuo"] .mantine-Card-root {
box-shadow: var(--mantine-shadow-sm);
}
[data-theme="skeuo"] .mantine-Modal-content {
box-shadow: var(--mantine-shadow-xl);
}
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-Modal-header {
background-color: #d5dae0;
background-image: var(--skeuo-brush),
linear-gradient(180deg, #f2f4f6 0%, #d9dde2 48%, #c2c8cf 52%, #ced3da 100%);
border-bottom: 1px solid #8f97a1;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-Modal-header {
background-color: #2b2d31;
background-image: var(--skeuo-brush),
linear-gradient(180deg, #3c3f45 0%, #2c2e33 48%, #222428 52%, #26282c 100%);
border-bottom: 1px solid #0a0b0c;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
/* ---------- AppShell: brushed-metal header, machined dock ---------- */
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-AppShell-header {
background-color: #b7bdc5;
background-image: var(--skeuo-noise), var(--skeuo-brush),
linear-gradient(180deg, #f4f6f8 0%, #d3d8de 45%, #b3bac3 50%, #a9b0ba 96%, #99a1ab 100%);
border-bottom: 1px solid #6f7681;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.75),
0 1px 0 rgba(255, 255, 255, 0.25), 0 2px 8px rgba(46, 41, 33, 0.3);
}
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-AppShell-header .mantine-Title-root {
color: #2e333a;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-AppShell-header {
background-color: #26282c;
background-image: var(--skeuo-noise), var(--skeuo-brush),
linear-gradient(180deg, #3e4147 0%, #2c2e33 45%, #212327 50%, #1d1f22 100%);
border-bottom: 1px solid #0a0b0c;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14),
0 2px 10px rgba(0, 0, 0, 0.6);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-AppShell-header .mantine-Title-root {
color: #e9e6df;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.8);
}
[data-theme="skeuo"] .mantine-AppShell-navbar {
background-color: transparent;
}
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-AppShell-navbar {
background-image: linear-gradient(90deg, #e3dfd3 0%, #ece8dd 100%);
border-right: 1px solid #b3ab99;
box-shadow: inset -1px 0 0 rgba(255, 255, 255, 0.6);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-AppShell-navbar {
background-image: linear-gradient(90deg, #1d1f22 0%, #222428 100%);
border-right: 1px solid #0d0e10;
box-shadow: inset -1px 0 0 rgba(255, 255, 255, 0.05);
}
/* mobile bottom dock (Paper nav) — polished metal slab */
[data-theme="skeuo"][data-mantine-color-scheme="light"] nav[role="navigation"].mantine-Paper-root {
background-color: #d9dde2;
background-image: var(--skeuo-brush),
linear-gradient(180deg, #f3f5f7 0%, #d8dce1 48%, #c3c9d0 52%, #cdd2d9 100%);
border: 1px solid #868e98;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8),
0 4px 10px rgba(46, 41, 33, 0.28), 0 10px 24px rgba(46, 41, 33, 0.18);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] nav[role="navigation"].mantine-Paper-root {
background-color: #26282c;
background-image: var(--skeuo-brush),
linear-gradient(180deg, #393c42 0%, #2a2c30 48%, #202226 52%, #24262a 100%);
border: 1px solid #0a0b0c;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12),
0 4px 12px rgba(0, 0, 0, 0.55), 0 12px 28px rgba(0, 0, 0, 0.4);
}
/* ---------- Tabs & SegmentedControl: machined switches ---------- */
[data-theme="skeuo"] .mantine-Tabs-list {
--tabs-list-border-size: 1px;
}
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-Tabs-tab:where([data-active]) {
box-shadow: inset 0 1px 3px rgba(46, 41, 33, 0.12);
}
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-SegmentedControl-root {
background-color: #d3cec0;
background-image: linear-gradient(180deg, #c8c2b2 0%, #d6d1c3 30%, #dedacd 100%);
border: 1px solid #a1977f;
border-radius: 100px;
box-shadow: inset 0 1px 3px rgba(46, 41, 33, 0.25),
0 1px 0 rgba(255, 255, 255, 0.65);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-SegmentedControl-root {
background-color: #141518;
background-image: linear-gradient(180deg, #0e0f11 0%, #16171a 40%, #1b1c20 100%);
border: 1px solid #0a0b0c;
border-radius: 100px;
box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6),
0 1px 0 rgba(255, 255, 255, 0.06);
}
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-SegmentedControl-indicator {
background-color: #f4f1e9;
background-image: linear-gradient(180deg, #fdfcf8 0%, #efebe0 48%, #e0dbcc 52%, #d8d2c3 100%);
border: 1px solid #998f7a;
border-radius: 100px;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9),
0 1px 3px rgba(46, 41, 33, 0.35);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-SegmentedControl-indicator {
background-color: #34363c;
background-image: linear-gradient(180deg, #45484f 0%, #34373c 48%, #2a2c31 52%, #26282c 100%);
border: 1px solid #0d0e10;
border-radius: 100px;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14),
0 1px 3px rgba(0, 0, 0, 0.6);
}
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-SegmentedControl-label {
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.55);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-SegmentedControl-label {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.55);
}
/* ---------- App hooks: tab strip, nav tiles, see-all, match card ---------- */
/* tab strip — flat while resting; docked = brushed band, same alloy as
the header */
[data-theme="skeuo"][data-mantine-color-scheme="light"] .flxn-tabs[data-stuck] {
background-color: #ccd1d8;
background-image: var(--skeuo-brush),
linear-gradient(180deg, #eef0f3 0%, #d3d8de 48%, #bfc5cd 52%, #c8cdd4 100%);
border-bottom-color: #868e98;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .flxn-tabs[data-stuck] {
background-color: #26282c;
background-image: var(--skeuo-brush),
linear-gradient(180deg, #35383e 0%, #292b2f 48%, #202226 52%, #24262a 100%);
border-bottom-color: #0a0b0c;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
[data-theme="skeuo"] .flxn-tab {
font-weight: 700;
}
[data-theme="skeuo"][data-mantine-color-scheme="light"] .flxn-tab {
color: #4d5560;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .flxn-tab {
color: #94918a;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.6);
}
/* active tab — accent-tinted engraving ink; the pressed-well chrome only
appears when the strip is DOCKED (resting strip stays flat paper) */
[data-theme="skeuo"][data-mantine-color-scheme="light"] .flxn-tab[data-active] {
/* ≥4.5:1 over both the page and the brushed strip for all eight accents */
color: color-mix(in srgb, var(--mantine-primary-color-filled) 45%, black);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .flxn-tab[data-active] {
color: color-mix(in srgb, var(--mantine-primary-color-filled) 50%, white);
}
[data-theme="skeuo"][data-mantine-color-scheme="light"]
.flxn-tabs[data-stuck]
.flxn-tab[data-active] {
background-image: linear-gradient(
180deg,
rgba(46, 41, 33, 0.12) 0%,
rgba(46, 41, 33, 0.03) 40%,
rgba(255, 255, 255, 0.14) 100%
);
box-shadow: inset 0 2px 4px rgba(46, 41, 33, 0.22);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"]
.flxn-tabs[data-stuck]
.flxn-tab[data-active] {
background-image: linear-gradient(
180deg,
rgba(0, 0, 0, 0.35) 0%,
rgba(0, 0, 0, 0.1) 40%,
rgba(255, 255, 255, 0.04) 100%
);
box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.55);
}
/* indicator — plain accent underline, no glow */
[data-theme="skeuo"] .flxn-tabs-indicator {
border-bottom: 3px solid var(--mantine-primary-color-filled);
box-shadow: none;
}
/* nav grid tiles — raised machined slabs, pressed state sinks */
[data-theme="skeuo"][data-mantine-color-scheme="light"] .flxn-tile {
background-color: #eeebe0;
background-image: linear-gradient(180deg, #fdfcf8 0%, #f0ede2 48%, #e2ddce 52%, #d8d2c3 100%);
border: 1px solid #9d947f;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.85),
inset 0 -1px 0 rgba(0, 0, 0, 0.08), 0 1px 2px rgba(46, 41, 33, 0.22),
0 4px 10px rgba(46, 41, 33, 0.12);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .flxn-tile {
background-color: #2b2d31;
background-image: linear-gradient(180deg, #3d4046 0%, #2e3035 48%, #25272b 52%, #202226 100%);
border: 1px solid #0d0e10;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12),
inset 0 -1px 0 rgba(0, 0, 0, 0.4), 0 1px 2px rgba(0, 0, 0, 0.5),
0 4px 12px rgba(0, 0, 0, 0.35);
}
[data-theme="skeuo"][data-mantine-color-scheme="light"] .flxn-tile:active {
background-image: linear-gradient(180deg, #c8c2b1 0%, #d5d0c0 48%, #e4e0d3 52%, #efece2 100%);
box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.26),
0 1px 0 rgba(255, 255, 255, 0.6);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .flxn-tile:active {
background-image: linear-gradient(180deg, #17181b 0%, #202226 48%, #2b2d32 52%, #34363c 100%);
box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.65),
0 1px 0 rgba(255, 255, 255, 0.06);
}
/* tile icon — flat anodized tint */
[data-theme="skeuo"] .flxn-tile-icon {
color: var(--mantine-primary-color-light-color);
}
/* see-all — small glossy accent pill. Base fill stays primary-filled under
contrast text; the gloss is a translucent overlay so it never shifts the
fill toward the text color. */
[data-theme="skeuo"] .flxn-see-all {
border-radius: 100px;
border: 1px solid color-mix(in srgb, var(--mantine-primary-color-filled) 50%, black);
background-color: var(--mantine-primary-color-filled);
background-image: linear-gradient(
180deg,
rgba(255, 255, 255, 0.28) 0%,
rgba(255, 255, 255, 0.08) 48%,
rgba(255, 255, 255, 0) 52%,
rgba(0, 0, 0, 0.16) 100%
);
color: var(--mantine-primary-color-contrast);
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45),
0 1px 2px rgba(0, 0, 0, 0.3);
}
@media (hover: hover) {
[data-theme="skeuo"] .flxn-see-all:hover {
background-image: linear-gradient(
180deg,
rgba(255, 255, 255, 0.36) 0%,
rgba(255, 255, 255, 0.14) 48%,
rgba(255, 255, 255, 0.05) 52%,
rgba(0, 0, 0, 0.12) 100%
);
}
}
/* match card raised, reaction tray recessed as an inset well beneath it */
[data-theme="skeuo"][data-mantine-color-scheme="light"] .flxn-match-card {
background-color: #f6f3ec;
background-image: linear-gradient(180deg, #fcfbf7 0%, #f1eee5 100%);
border: 1px solid #c2bbaa;
box-shadow: var(--mantine-shadow-sm);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .flxn-match-card {
background-color: #222428;
background-image: linear-gradient(180deg, #27292e 0%, #1e2023 100%);
border: 1px solid #0d0e10;
box-shadow: var(--mantine-shadow-sm);
}
[data-theme="skeuo"][data-mantine-color-scheme="light"] .flxn-reaction-row {
background-color: #ddd7c8;
background-image: linear-gradient(
180deg,
rgba(46, 41, 33, 0.14) 0%,
rgba(46, 41, 33, 0.03) 45%,
rgba(255, 255, 255, 0.25) 100%
);
border: 1px solid #c2bbaa;
box-shadow: inset 0 2px 4px rgba(46, 41, 33, 0.18),
0 1px 0 rgba(255, 255, 255, 0.55);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .flxn-reaction-row {
background-color: #17181b;
background-image: linear-gradient(
180deg,
rgba(0, 0, 0, 0.45) 0%,
rgba(0, 0, 0, 0.1) 45%,
rgba(255, 255, 255, 0.04) 100%
);
border: 1px solid #0d0e10;
box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.55),
0 1px 0 rgba(255, 255, 255, 0.05);
}
/* ---------- Badges: glossy enamel pills ---------- */
[data-theme="skeuo"] .mantine-Badge-root {
border: 1px solid rgba(0, 0, 0, 0.28);
background-image: linear-gradient(
180deg,
rgba(255, 255, 255, 0.4) 0%,
rgba(255, 255, 255, 0.12) 48%,
rgba(0, 0, 0, 0.06) 52%,
rgba(0, 0, 0, 0.16) 100%
);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35),
0 1px 2px rgba(0, 0, 0, 0.25);
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
}
/* accent-fill on-colors (Mantine hardcodes white on filled variants,
selected combobox options and check glyphs) */
[data-theme="skeuo"] .mantine-Badge-root[data-variant="filled"],
[data-theme="skeuo"] .mantine-Badge-root:not([data-variant]),
[data-theme="skeuo"] .mantine-ThemeIcon-root[data-variant="filled"],
[data-theme="skeuo"] .mantine-ThemeIcon-root:not([data-variant]) {
color: var(--mantine-primary-color-contrast);
}
[data-theme="skeuo"] [data-combobox-selected] {
color: var(--mantine-primary-color-contrast);
}
[data-theme="skeuo"] .mantine-Checkbox-root {
--checkbox-icon-color: var(--mantine-primary-color-contrast);
}
[data-theme="skeuo"] .mantine-Radio-root {
--radio-icon-color: var(--mantine-primary-color-contrast);
}
/* ---------- Toasts: glossy pills ---------- */
[data-theme="skeuo"] .mantine-Notification-root {
border-radius: 100px;
padding-block: 0.5rem;
}
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-Notification-root {
background-color: #f8f6f0;
background-image: linear-gradient(180deg, #fefdfb 0%, #f4f1e9 48%, #e9e5d8 52%, #efece2 100%);
border: 1px solid #a99f8c;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9),
0 3px 8px rgba(46, 41, 33, 0.28), 0 10px 24px rgba(46, 41, 33, 0.18);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-Notification-root {
background-color: #2b2d31;
background-image: linear-gradient(180deg, #3b3e44 0%, #2c2e33 48%, #232529 52%, #27292d 100%);
border: 1px solid #0a0b0c;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12),
0 3px 10px rgba(0, 0, 0, 0.55), 0 12px 28px rgba(0, 0, 0, 0.4);
}
/* ---------- Switch: chrome knob in a well ---------- */
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-Switch-track {
border: 1px solid #968c77;
background-image: linear-gradient(180deg, rgba(0, 0, 0, 0.18) 0%, rgba(0, 0, 0, 0.04) 40%, rgba(255, 255, 255, 0.1) 100%);
box-shadow: inset 0 1px 3px rgba(46, 41, 33, 0.3);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-Switch-track {
border: 1px solid #0a0b0c;
background-image: linear-gradient(180deg, rgba(0, 0, 0, 0.45) 0%, rgba(0, 0, 0, 0.1) 40%, rgba(255, 255, 255, 0.05) 100%);
box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
}
[data-theme="skeuo"] .mantine-Switch-thumb {
border: 1px solid rgba(0, 0, 0, 0.4);
background-color: #f2f2f0;
background-image: linear-gradient(180deg, #ffffff 0%, #e8e8e4 48%, #d4d4ce 52%, #dededa 100%);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9),
0 1px 3px rgba(0, 0, 0, 0.4);
}
/* ---------- Checkbox / Radio: inset wells, glossy when checked ---------- */
[data-theme="skeuo"] .mantine-Checkbox-input,
[data-theme="skeuo"] .mantine-Radio-radio {
border: 1px solid var(--mantine-color-default-border);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2);
}
[data-theme="skeuo"] .mantine-Checkbox-input:checked,
[data-theme="skeuo"] .mantine-Radio-radio:checked {
background-image: linear-gradient(
180deg,
rgba(255, 255, 255, 0.35) 0%,
rgba(255, 255, 255, 0.08) 48%,
rgba(0, 0, 0, 0.1) 52%,
rgba(0, 0, 0, 0.2) 100%
);
border-color: color-mix(in srgb, var(--mantine-primary-color-filled) 60%, black);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35),
0 1px 2px rgba(0, 0, 0, 0.25);
}
/* ---------- Progress: candy bar in a machined groove ---------- */
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-Progress-root {
background-color: #cfc9ba;
background-image: linear-gradient(180deg, rgba(0, 0, 0, 0.14), transparent 60%);
border: 1px solid #a1977f;
box-shadow: inset 0 1px 3px rgba(46, 41, 33, 0.28);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-Progress-root {
background-color: #121316;
background-image: linear-gradient(180deg, rgba(0, 0, 0, 0.5), transparent 60%);
border: 1px solid #0a0b0c;
box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
}
[data-theme="skeuo"] .mantine-Progress-section {
background-image: linear-gradient(
180deg,
rgba(255, 255, 255, 0.45) 0%,
rgba(255, 255, 255, 0.12) 48%,
rgba(0, 0, 0, 0.08) 52%,
rgba(0, 0, 0, 0.18) 100%
);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4);
}
/* ---------- Misc: dividers, tooltips, avatars, alerts, skeleton ---------- */
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-Divider-root {
--divider-color: #c4bcab;
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.65);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-Divider-root {
--divider-color: #0f1012;
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.05);
}
[data-theme="skeuo"] .mantine-Tooltip-tooltip {
background-color: #26282c;
background-image: linear-gradient(180deg, #3a3d43 0%, #26282c 55%, #1e2023 100%);
color: #ece9e2;
border: 1px solid #0d0e10;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.6);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12),
0 2px 6px rgba(0, 0, 0, 0.4);
}
/* polished bezel: dark rim + inner highlight ring */
[data-theme="skeuo"] .mantine-ThemeIcon-root {
border: 1px solid rgba(0, 0, 0, 0.3);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35),
inset 0 0 0 1px rgba(255, 255, 255, 0.18),
0 1px 2px rgba(0, 0, 0, 0.25);
}
/* avatar bezel lives on the frame; the metal rim is --flxn-avatar-border */
[data-theme="skeuo"] .flxn-avatar-frame {
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35),
inset 0 0 0 1px rgba(255, 255, 255, 0.18),
0 1px 2px rgba(0, 0, 0, 0.25);
}
[data-theme="skeuo"] .mantine-ThemeIcon-root[data-variant="filled"] {
background-image: linear-gradient(
180deg,
rgba(255, 255, 255, 0.35) 0%,
rgba(255, 255, 255, 0.08) 48%,
rgba(0, 0, 0, 0.08) 52%,
rgba(0, 0, 0, 0.18) 100%
);
}
[data-theme="skeuo"] .mantine-Alert-root {
border: 1px solid rgba(0, 0, 0, 0.2);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25),
0 1px 3px rgba(0, 0, 0, 0.15);
}
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-Skeleton-root::after {
background-color: #ddd8ca;
}
/* ---------- PinInput inherits Input wells; keep digits engraved ---------- */
[data-theme="skeuo"][data-mantine-color-scheme="light"] .mantine-PinInput-field {
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
}
[data-theme="skeuo"][data-mantine-color-scheme="dark"] .mantine-PinInput-field {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.6);
}
+7
View File
@@ -0,0 +1,7 @@
// Aggregates all theme CSS. Import once from the client provider tree only —
// never from server-fn modules.
// The glob stays in theme-styles.ts: Vite hoists eager-glob imports above a
// module's own static imports, which would load theme CSS before base/hooks.
import "./base.css";
import "./hooks.css";
import "./theme-styles";
+19
View File
@@ -0,0 +1,19 @@
import { msg } from "@lingui/core/macro";
import type { ThemeManifest } from "../types";
const manifest: ThemeManifest = {
id: "terminal",
label: msg`Terminal`,
colorSchemes: "dark",
fontLinks: [
"https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600;700&display=swap",
],
chromeColors: {
dark: { base: "#050805", dimmed: "#020402" },
},
mantine: {
defaultRadius: 0,
},
};
export default manifest;
+599
View File
@@ -0,0 +1,599 @@
/* CRT Terminal — phosphor monochrome in the user's accent hue, dark only.
The accent picks the tube: green = classic CRT, orange = amber terminal,
blue = blue phosphor, etc. Error stays red-phosphor. */
/* 1. Tokens, any scheme */
[data-theme="terminal"][data-theme] {
--mantine-font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
--mantine-font-family-headings: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
--mantine-font-family-monospace: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
--mantine-heading-font-weight: 600;
--mantine-radius-default: 0;
--mantine-radius-xs: 0;
--mantine-radius-sm: 0;
--mantine-radius-md: 0;
--mantine-radius-lg: 0;
--mantine-radius-xl: 0;
--flxn-avatar-radius: 0;
--flxn-avatar-border: 1px solid var(--mantine-primary-color-6);
--flxn-badge-radius: 0;
/* flat / glow elevation — no soft shadows (sm is preview-contract: neutral) */
--mantine-shadow-xs: 0 0 0 1px color-mix(in srgb, var(--mantine-primary-color-5) 12%, transparent);
--mantine-shadow-sm: 0 0 0 1px rgba(200, 212, 200, 0.15);
--mantine-shadow-md: 0 0 0 1px color-mix(in srgb, var(--mantine-primary-color-5) 20%, transparent),
0 0 12px color-mix(in srgb, var(--mantine-primary-color-5) 12%, transparent);
--mantine-shadow-lg: 0 0 0 1px color-mix(in srgb, var(--mantine-primary-color-5) 25%, transparent),
0 0 18px color-mix(in srgb, var(--mantine-primary-color-5) 15%, transparent);
--mantine-shadow-xl: 0 0 0 1px color-mix(in srgb, var(--mantine-primary-color-5) 30%, transparent),
0 0 26px color-mix(in srgb, var(--mantine-primary-color-5) 18%, transparent);
/* eight phosphor ramps — bright tube core at 4-5, dim glass below */
--mantine-color-green-0: #eaffea;
--mantine-color-green-1: #ccffcc;
--mantine-color-green-2: #a8ffa8;
--mantine-color-green-3: #7dff7d;
--mantine-color-green-4: #55ff55;
--mantine-color-green-5: #33ff33;
--mantine-color-green-6: #21d621;
--mantine-color-green-7: #17a317;
--mantine-color-green-8: #0e700e;
--mantine-color-green-9: #084008;
--mantine-color-red-0: #ffeaea;
--mantine-color-red-1: #ffcccc;
--mantine-color-red-2: #ffa8a8;
--mantine-color-red-3: #ff7d7d;
--mantine-color-red-4: #ff5555;
--mantine-color-red-5: #ff3333;
--mantine-color-red-6: #d62121;
--mantine-color-red-7: #a31717;
--mantine-color-red-8: #700e0e;
--mantine-color-red-9: #400808;
--mantine-color-blue-0: #eaf4ff;
--mantine-color-blue-1: #cce6ff;
--mantine-color-blue-2: #a8d5ff;
--mantine-color-blue-3: #7dc1ff;
--mantine-color-blue-4: #55aeff;
--mantine-color-blue-5: #339cff;
--mantine-color-blue-6: #217fd6;
--mantine-color-blue-7: #1760a3;
--mantine-color-blue-8: #0e4170;
--mantine-color-blue-9: #082440;
--mantine-color-yellow-0: #fffbea;
--mantine-color-yellow-1: #fff6cc;
--mantine-color-yellow-2: #fff0a8;
--mantine-color-yellow-3: #ffe97d;
--mantine-color-yellow-4: #ffe255;
--mantine-color-yellow-5: #ffdb33;
--mantine-color-yellow-6: #d6b421;
--mantine-color-yellow-7: #a38817;
--mantine-color-yellow-8: #705d0e;
--mantine-color-yellow-9: #403408;
--mantine-color-orange-0: #fff5e0;
--mantine-color-orange-1: #ffeab8;
--mantine-color-orange-2: #ffdd85;
--mantine-color-orange-3: #ffcf5c;
--mantine-color-orange-4: #ffc133;
--mantine-color-orange-5: #ffb000;
--mantine-color-orange-6: #d69200;
--mantine-color-orange-7: #a37000;
--mantine-color-orange-8: #704d00;
--mantine-color-orange-9: #402c00;
--mantine-color-grape-0: #f6eaff;
--mantine-color-grape-1: #eaccff;
--mantine-color-grape-2: #dba8ff;
--mantine-color-grape-3: #c97dff;
--mantine-color-grape-4: #b855ff;
--mantine-color-grape-5: #a833ff;
--mantine-color-grape-6: #8c21d6;
--mantine-color-grape-7: #6a17a3;
--mantine-color-grape-8: #490e70;
--mantine-color-grape-9: #290840;
--mantine-color-pink-0: #ffeaf6;
--mantine-color-pink-1: #ffccea;
--mantine-color-pink-2: #ffa8db;
--mantine-color-pink-3: #ff7dc9;
--mantine-color-pink-4: #ff55b8;
--mantine-color-pink-5: #ff33a8;
--mantine-color-pink-6: #d6218c;
--mantine-color-pink-7: #a3176a;
--mantine-color-pink-8: #700e49;
--mantine-color-pink-9: #400829;
--mantine-color-lime-0: #f6ffea;
--mantine-color-lime-1: #eaffcc;
--mantine-color-lime-2: #dbffa8;
--mantine-color-lime-3: #c9ff7d;
--mantine-color-lime-4: #b8ff55;
--mantine-color-lime-5: #a8ff33;
--mantine-color-lime-6: #8cd621;
--mantine-color-lime-7: #6aa317;
--mantine-color-lime-8: #49700e;
--mantine-color-lime-9: #294008;
/* preview contract (neutral-phosphor; mirrored in scheme block below) */
--mantine-color-body: #050505;
--mantine-color-text: #c8d4c8;
--mantine-color-dimmed: #7c887c;
--mantine-color-default-border: #333a33;
}
/* 2. Scheme tokens (engine forces dark) */
[data-theme="terminal"][data-theme][data-mantine-color-scheme="dark"] {
--mantine-color-body: #050505;
--mantine-color-text: #c8d4c8;
--mantine-color-bright: #e8efe8;
--mantine-color-dimmed: #7c887c;
--mantine-color-anchor: var(--mantine-primary-color-3);
--mantine-color-placeholder: #5a655a;
--mantine-color-error: #ff5c5c;
--mantine-color-default: #0b0d0b;
--mantine-color-default-hover: #141714;
--mantine-color-default-color: #c8d4c8;
--mantine-color-default-border: #333a33;
/* per-color variant vars, phosphor-tuned (beat Mantine's :root[scheme]) */
--mantine-color-green-filled: var(--mantine-color-green-5);
--mantine-color-green-filled-hover: var(--mantine-color-green-4);
--mantine-color-green-light: color-mix(in srgb, var(--mantine-color-green-5) 12%, transparent);
--mantine-color-green-light-hover: color-mix(in srgb, var(--mantine-color-green-5) 20%, transparent);
--mantine-color-green-light-color: var(--mantine-color-green-4);
--mantine-color-green-outline: var(--mantine-color-green-5);
--mantine-color-green-outline-hover: color-mix(in srgb, var(--mantine-color-green-5) 12%, transparent);
--mantine-color-green-text: var(--mantine-color-green-4);
--mantine-color-red-filled: var(--mantine-color-red-5);
--mantine-color-red-filled-hover: var(--mantine-color-red-4);
--mantine-color-red-light: color-mix(in srgb, var(--mantine-color-red-5) 12%, transparent);
--mantine-color-red-light-hover: color-mix(in srgb, var(--mantine-color-red-5) 20%, transparent);
--mantine-color-red-light-color: var(--mantine-color-red-4);
--mantine-color-red-outline: var(--mantine-color-red-5);
--mantine-color-red-outline-hover: color-mix(in srgb, var(--mantine-color-red-5) 12%, transparent);
--mantine-color-red-text: var(--mantine-color-red-4);
--mantine-color-blue-filled: var(--mantine-color-blue-5);
--mantine-color-blue-filled-hover: var(--mantine-color-blue-4);
--mantine-color-blue-light: color-mix(in srgb, var(--mantine-color-blue-5) 12%, transparent);
--mantine-color-blue-light-hover: color-mix(in srgb, var(--mantine-color-blue-5) 20%, transparent);
--mantine-color-blue-light-color: var(--mantine-color-blue-4);
--mantine-color-blue-outline: var(--mantine-color-blue-5);
--mantine-color-blue-outline-hover: color-mix(in srgb, var(--mantine-color-blue-5) 12%, transparent);
--mantine-color-blue-text: var(--mantine-color-blue-4);
--mantine-color-yellow-filled: var(--mantine-color-yellow-5);
--mantine-color-yellow-filled-hover: var(--mantine-color-yellow-4);
--mantine-color-yellow-light: color-mix(in srgb, var(--mantine-color-yellow-5) 12%, transparent);
--mantine-color-yellow-light-hover: color-mix(in srgb, var(--mantine-color-yellow-5) 20%, transparent);
--mantine-color-yellow-light-color: var(--mantine-color-yellow-4);
--mantine-color-yellow-outline: var(--mantine-color-yellow-5);
--mantine-color-yellow-outline-hover: color-mix(in srgb, var(--mantine-color-yellow-5) 12%, transparent);
--mantine-color-yellow-text: var(--mantine-color-yellow-4);
--mantine-color-orange-filled: var(--mantine-color-orange-5);
--mantine-color-orange-filled-hover: var(--mantine-color-orange-4);
--mantine-color-orange-light: color-mix(in srgb, var(--mantine-color-orange-5) 12%, transparent);
--mantine-color-orange-light-hover: color-mix(in srgb, var(--mantine-color-orange-5) 20%, transparent);
--mantine-color-orange-light-color: var(--mantine-color-orange-4);
--mantine-color-orange-outline: var(--mantine-color-orange-5);
--mantine-color-orange-outline-hover: color-mix(in srgb, var(--mantine-color-orange-5) 12%, transparent);
--mantine-color-orange-text: var(--mantine-color-orange-4);
--mantine-color-grape-filled: var(--mantine-color-grape-5);
--mantine-color-grape-filled-hover: var(--mantine-color-grape-4);
--mantine-color-grape-light: color-mix(in srgb, var(--mantine-color-grape-5) 12%, transparent);
--mantine-color-grape-light-hover: color-mix(in srgb, var(--mantine-color-grape-5) 20%, transparent);
--mantine-color-grape-light-color: var(--mantine-color-grape-4);
--mantine-color-grape-outline: var(--mantine-color-grape-5);
--mantine-color-grape-outline-hover: color-mix(in srgb, var(--mantine-color-grape-5) 12%, transparent);
--mantine-color-grape-text: var(--mantine-color-grape-4);
--mantine-color-pink-filled: var(--mantine-color-pink-5);
--mantine-color-pink-filled-hover: var(--mantine-color-pink-4);
--mantine-color-pink-light: color-mix(in srgb, var(--mantine-color-pink-5) 12%, transparent);
--mantine-color-pink-light-hover: color-mix(in srgb, var(--mantine-color-pink-5) 20%, transparent);
--mantine-color-pink-light-color: var(--mantine-color-pink-4);
--mantine-color-pink-outline: var(--mantine-color-pink-5);
--mantine-color-pink-outline-hover: color-mix(in srgb, var(--mantine-color-pink-5) 12%, transparent);
--mantine-color-pink-text: var(--mantine-color-pink-4);
--mantine-color-lime-filled: var(--mantine-color-lime-5);
--mantine-color-lime-filled-hover: var(--mantine-color-lime-4);
--mantine-color-lime-light: color-mix(in srgb, var(--mantine-color-lime-5) 12%, transparent);
--mantine-color-lime-light-hover: color-mix(in srgb, var(--mantine-color-lime-5) 20%, transparent);
--mantine-color-lime-light-color: var(--mantine-color-lime-4);
--mantine-color-lime-outline: var(--mantine-color-lime-5);
--mantine-color-lime-outline-hover: color-mix(in srgb, var(--mantine-color-lime-5) 12%, transparent);
--mantine-color-lime-text: var(--mantine-color-lime-4);
/* app tokens — monochrome ranks in the tube's hue */
--flxn-gold: var(--mantine-primary-color-4);
--flxn-silver: var(--mantine-primary-color-6);
--flxn-bronze: var(--mantine-primary-color-7);
--flxn-toast-success: var(--mantine-primary-color-4);
--flxn-toast-error: #ff7a7a;
--flxn-podium-1-bg: color-mix(in srgb, var(--mantine-primary-color-5) 10%, #050505);
--flxn-podium-1-border: var(--mantine-primary-color-5);
--flxn-podium-1-label: var(--mantine-primary-color-3);
--flxn-podium-2-bg: color-mix(in srgb, var(--mantine-primary-color-5) 6%, #050505);
--flxn-podium-2-border: var(--mantine-primary-color-6);
--flxn-podium-2-label: var(--mantine-primary-color-5);
--flxn-podium-3-bg: color-mix(in srgb, var(--mantine-primary-color-5) 3%, #050505);
--flxn-podium-3-border: var(--mantine-primary-color-7);
--flxn-podium-3-label: var(--mantine-primary-color-6);
--flxn-showcase-bg: color-mix(in srgb, var(--mantine-primary-color-5) 5%, #050505);
--flxn-overlay: rgba(0, 0, 0, 0.74);
--flxn-dotgrid-color: color-mix(in srgb, var(--mantine-primary-color-5) 15%, transparent);
--flxn-pop-shadow: color-mix(in srgb, var(--mantine-primary-color-5) 20%, transparent);
}
/* App-side phosphor text/borders: re-driven from the accent on body so the
preview contract above stays neutral (previews render in divs, no body). */
[data-theme="terminal"] body {
color: var(--mantine-primary-color-4);
--mantine-color-text: var(--mantine-primary-color-4);
--mantine-color-bright: var(--mantine-primary-color-2);
--mantine-color-dimmed: color-mix(in srgb, var(--mantine-primary-color-4) 62%, #050505);
--mantine-color-placeholder: color-mix(in srgb, var(--mantine-primary-color-4) 45%, #050505);
--mantine-color-default-color: var(--mantine-primary-color-4);
--mantine-color-default-border: color-mix(in srgb, var(--mantine-primary-color-5) 55%, #050505);
--mantine-primary-color-contrast: #050505;
--mantine-shadow-sm: 0 0 0 1px color-mix(in srgb, var(--mantine-primary-color-5) 15%, transparent);
}
/* 3. Component restyles */
/* selection & focus: inverse video */
[data-theme="terminal"] ::selection {
background: var(--mantine-primary-color-filled);
color: #050505;
text-shadow: none;
}
[data-theme="terminal"] :focus-visible {
outline: 1px solid var(--mantine-primary-color-4);
outline-offset: 1px;
}
/* phosphor glow on headings */
[data-theme="terminal"] :is(h1, h2, h3, h4, h5, h6),
[data-theme="terminal"] .mantine-Title-root {
color: var(--mantine-primary-color-4);
text-shadow: 0 0 6px color-mix(in srgb, var(--mantine-primary-color-5) 50%, transparent),
0 0 18px color-mix(in srgb, var(--mantine-primary-color-5) 20%, transparent);
}
[data-theme="terminal"] a {
text-underline-offset: 3px;
}
/* surfaces: flat panels with 1px phosphor rule */
[data-theme="terminal"] .mantine-Paper-root,
[data-theme="terminal"] .mantine-Card-root {
background-color: color-mix(in srgb, var(--mantine-primary-color-5) 4%, #050505);
border: 1px solid var(--mantine-color-default-border);
box-shadow: none;
}
[data-theme="terminal"] .mantine-AppShell-header,
[data-theme="terminal"] .mantine-AppShell-navbar {
background-color: rgba(5, 5, 5, 0.96);
border-color: var(--mantine-color-default-border);
}
/* buttons: bracketed bordered blocks */
[data-theme="terminal"] .mantine-Button-root {
border: 1px solid var(--mantine-primary-color-filled);
text-transform: uppercase;
letter-spacing: 0.06em;
font-weight: 600;
}
[data-theme="terminal"] .mantine-Button-root .mantine-Button-label::before {
content: "[ ";
opacity: 0.55;
}
[data-theme="terminal"] .mantine-Button-root .mantine-Button-label::after {
content: " ]";
opacity: 0.55;
}
[data-theme="terminal"] .mantine-Button-root[data-variant="default"],
[data-theme="terminal"] .mantine-Button-root[data-variant="outline"],
[data-theme="terminal"] .mantine-Button-root[data-variant="light"],
[data-theme="terminal"] .mantine-Button-root[data-variant="subtle"] {
background-color: transparent;
color: var(--mantine-primary-color-4);
}
[data-theme="terminal"] .mantine-Button-root[data-variant="subtle"] {
border-color: transparent;
}
[data-theme="terminal"] .mantine-Button-root[data-variant="filled"] {
background-color: var(--mantine-primary-color-filled);
color: #050505;
box-shadow: 0 0 10px color-mix(in srgb, var(--mantine-primary-color-5) 35%, transparent);
}
@media (hover: hover) {
[data-theme="terminal"] .mantine-Button-root:not([data-disabled]):hover {
background-color: var(--mantine-primary-color-filled);
color: #050505;
text-shadow: none;
}
}
[data-theme="terminal"] .mantine-Button-root[data-disabled] {
border-color: color-mix(in srgb, var(--mantine-primary-color-5) 45%, #050505);
color: color-mix(in srgb, var(--mantine-primary-color-5) 35%, #050505);
background-color: transparent;
}
[data-theme="terminal"] .mantine-ActionIcon-root {
border: 1px solid var(--mantine-color-default-border);
}
[data-theme="terminal"] .mantine-ActionIcon-root[data-variant="filled"] {
border-color: var(--mantine-primary-color-filled);
background-color: var(--mantine-primary-color-filled);
color: #050505;
}
[data-theme="terminal"] .mantine-ActionIcon-root[data-variant="subtle"],
[data-theme="terminal"] .mantine-ActionIcon-root[data-variant="transparent"] {
border-color: transparent;
}
/* inputs: block-cursor terminal fields */
[data-theme="terminal"] .mantine-Input-input {
background-color: #030303;
border: 1px solid var(--mantine-color-default-border);
color: var(--mantine-primary-color-4);
caret-color: var(--mantine-primary-color-4);
caret-shape: block;
font-family: var(--mantine-font-family-monospace);
}
[data-theme="terminal"] .mantine-Input-input:focus,
[data-theme="terminal"] .mantine-Input-input:focus-within {
border-color: var(--mantine-primary-color-filled);
box-shadow: 0 0 8px color-mix(in srgb, var(--mantine-primary-color-5) 35%, transparent);
}
[data-theme="terminal"] .mantine-InputWrapper-label {
text-transform: uppercase;
letter-spacing: 0.06em;
font-size: 0.72em;
color: var(--mantine-color-dimmed);
}
[data-theme="terminal"] .mantine-PinInput-input {
background-color: #030303;
border-color: var(--mantine-color-default-border);
color: var(--mantine-primary-color-4);
}
/* tabs & segmented control: active = inverse video */
[data-theme="terminal"] .mantine-Tabs-list {
border-color: var(--mantine-color-default-border);
}
[data-theme="terminal"] .mantine-Tabs-tab {
text-transform: uppercase;
letter-spacing: 0.05em;
}
[data-theme="terminal"] .mantine-Tabs-tab[data-active] {
background-color: var(--mantine-primary-color-filled);
color: #050505;
border-color: var(--mantine-primary-color-filled);
}
[data-theme="terminal"] .mantine-SegmentedControl-root {
background-color: #030303;
border: 1px solid var(--mantine-color-default-border);
}
[data-theme="terminal"] .mantine-SegmentedControl-indicator {
background-color: var(--mantine-primary-color-filled);
box-shadow: none;
}
[data-theme="terminal"] .mantine-SegmentedControl-label[data-active] {
color: #050505;
}
/* badges, chips, indicators */
[data-theme="terminal"] .mantine-Badge-root {
background-color: transparent;
border: 1px solid var(--mantine-primary-color-6);
color: var(--mantine-primary-color-4);
text-transform: uppercase;
letter-spacing: 0.06em;
}
[data-theme="terminal"] .mantine-Badge-root[data-variant="filled"] {
background-color: var(--mantine-primary-color-filled);
border-color: var(--mantine-primary-color-filled);
color: #050505;
}
[data-theme="terminal"] .mantine-Indicator-indicator {
border: 1px solid #050505;
}
/* overlays */
[data-theme="terminal"] .mantine-Modal-content,
[data-theme="terminal"] .mantine-Popover-dropdown {
background-color: #050505;
border: 1px solid var(--mantine-primary-color-filled);
box-shadow: 0 0 24px color-mix(in srgb, var(--mantine-primary-color-5) 18%, transparent);
}
[data-theme="terminal"] .mantine-Modal-header {
background-color: #050505;
border-bottom: 1px solid var(--mantine-color-default-border);
}
[data-theme="terminal"] .mantine-Tooltip-tooltip {
background-color: var(--mantine-primary-color-filled);
color: #050505;
border-radius: 0;
}
/* selected option = inverse video (Mantine hardcodes white on the fill) */
[data-theme="terminal"] [data-combobox-selected] {
color: #050505;
}
[data-theme="terminal"] .mantine-Notification-root {
background-color: color-mix(in srgb, var(--mantine-primary-color-5) 4%, #050505);
border: 1px solid var(--mantine-primary-color-6);
box-shadow: 0 0 14px color-mix(in srgb, var(--mantine-primary-color-5) 18%, transparent);
}
[data-theme="terminal"] .mantine-Alert-root {
background-color: color-mix(in srgb, var(--mantine-primary-color-5) 8%, transparent);
border: 1px solid var(--mantine-primary-color-6);
}
/* controls */
[data-theme="terminal"] .mantine-Switch-track {
background-color: #030303;
border: 1px solid var(--mantine-color-default-border);
}
[data-theme="terminal"] .mantine-Switch-input:checked + .mantine-Switch-track {
background-color: var(--mantine-primary-color-filled);
border-color: var(--mantine-primary-color-filled);
}
[data-theme="terminal"] .mantine-Switch-thumb {
background-color: var(--mantine-primary-color-6);
border: none;
border-radius: 0;
}
[data-theme="terminal"] .mantine-Switch-input:checked + .mantine-Switch-track .mantine-Switch-thumb {
background-color: #050505;
}
[data-theme="terminal"] .mantine-Checkbox-input {
background-color: #030303;
border: 1px solid var(--mantine-color-default-border);
}
[data-theme="terminal"] .mantine-Checkbox-input:checked {
background-color: var(--mantine-primary-color-filled);
border-color: var(--mantine-primary-color-filled);
}
[data-theme="terminal"] .mantine-Checkbox-icon {
color: #050505;
}
[data-theme="terminal"] .mantine-Progress-root {
background-color: #0b0d0b;
border: 1px solid var(--mantine-color-default-border);
}
[data-theme="terminal"] .mantine-Progress-section {
background-color: var(--mantine-primary-color-filled);
}
[data-theme="terminal"] .mantine-Skeleton-root::before {
background-color: #0f110f;
}
[data-theme="terminal"] .mantine-Skeleton-root::after {
background-color: #191c19;
}
[data-theme="terminal"] .mantine-Divider-root {
--divider-color: var(--mantine-color-default-border);
}
/* nav */
[data-theme="terminal"] .mantine-NavLink-root[data-active] {
background-color: var(--mantine-primary-color-filled);
color: #050505;
}
[data-theme="terminal"] .mantine-NavLink-root[data-active] .mantine-NavLink-label,
[data-theme="terminal"] .mantine-NavLink-root[data-active] .mantine-NavLink-section {
color: #050505;
}
/* theme icons stay in the phosphor family */
[data-theme="terminal"] .mantine-ThemeIcon-root {
border: 1px solid var(--mantine-primary-color-5);
}
/* app hooks: swipeable tab strip — flat while resting; docked = ruled
readout band. Transparent resting top border keeps the strip height
stable across the state change. Active = inverse video. */
[data-theme="terminal"] .flxn-tabs {
border-top: 1px solid transparent;
}
[data-theme="terminal"] .flxn-tabs[data-stuck] {
background-color: var(--mantine-color-body);
border-top-color: var(--mantine-color-default-border);
border-bottom-color: var(--mantine-color-default-border);
}
[data-theme="terminal"] .flxn-tab.flxn-tab {
color: var(--mantine-color-dimmed);
text-transform: uppercase;
letter-spacing: 0.05em;
font-weight: 500;
}
[data-theme="terminal"] .flxn-tab.flxn-tab[data-active] {
background-color: var(--mantine-primary-color-filled);
color: #050505;
font-weight: 600;
text-shadow: none;
}
[data-theme="terminal"] .flxn-tabs-indicator.flxn-tabs-indicator {
border-bottom: 2px solid var(--mantine-primary-color-filled);
padding-inline: 0;
}
/* app hooks: nav grid tiles as bordered readout boxes */
[data-theme="terminal"] .flxn-tile {
background-color: color-mix(in srgb, var(--mantine-primary-color-5) 4%, #050505);
border: 1px solid var(--mantine-color-default-border);
border-radius: 0;
box-shadow: none;
}
[data-theme="terminal"] .flxn-tile-icon {
color: var(--mantine-primary-color-4);
}
/* app hooks: "see all" as a bracketed [link] */
[data-theme="terminal"] .flxn-see-all.flxn-see-all {
background-color: transparent;
border-radius: 0;
color: var(--mantine-primary-color-4);
text-transform: uppercase;
letter-spacing: 0.06em;
}
[data-theme="terminal"] .flxn-see-all.flxn-see-all::before {
content: "[ ";
opacity: 0.55;
}
[data-theme="terminal"] .flxn-see-all.flxn-see-all::after {
content: " ]";
opacity: 0.55;
}
@media (hover: hover) {
[data-theme="terminal"] .flxn-see-all.flxn-see-all:hover {
background-color: var(--mantine-primary-color-filled);
color: #050505;
}
}
/* app hooks: match card + reaction tray read as one bordered construction */
[data-theme="terminal"] .flxn-match-card {
background-color: color-mix(in srgb, var(--mantine-primary-color-5) 4%, #050505);
border: 1px solid var(--mantine-color-default-border);
box-shadow: none;
}
[data-theme="terminal"] .flxn-reaction-row.flxn-reaction-row {
background-color: color-mix(in srgb, var(--mantine-primary-color-5) 2%, #030303);
border: 1px solid var(--mantine-color-default-border);
border-radius: 0;
}
/* 4. Decoration: fixed scanline overlay + faint RGB aperture mask */
[data-theme="terminal"] body::before {
content: "";
position: fixed;
inset: 0;
z-index: 9998;
pointer-events: none;
background:
linear-gradient(rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0.22) 50%),
linear-gradient(90deg, rgba(255, 0, 0, 0.05), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.05));
background-size: 100% 2px, 3px 100%;
opacity: 0.35;
}
@media (prefers-reduced-motion: no-preference) {
[data-theme="terminal"] body::before {
animation: terminal-phosphor-drift 9s ease-in-out infinite;
}
@keyframes terminal-phosphor-drift {
0%, 100% { opacity: 0.35; }
50% { opacity: 0.3; }
}
}
+72
View File
@@ -0,0 +1,72 @@
import { useEffect } from "react";
import type { ThemeManifest } from "./types";
export const THEME_STORAGE_KEY = "flxn-theme";
export const THEME_SCHEME_STORAGE_KEY = "flxn-theme-scheme";
export const THEME_FONTS_STORAGE_KEY = "flxn-theme-fonts";
/** Applies html[data-theme] and mirrors theme id / forced scheme / font links
* to localStorage for the pre-hydration script in __root; injects the active
* theme's font stylesheets and removes stale ones from other themes.
* Writes nothing until `ready` (post-hydration with real metadata) so the
* pre-hydration default render can't clobber the mirror. */
const ThemeProvider = ({
manifest,
ready,
children,
}: {
manifest: ThemeManifest;
ready: boolean;
children: React.ReactNode;
}) => {
useEffect(() => {
if (!ready) return;
const root = document.documentElement;
if (root.getAttribute("data-theme") !== manifest.id) {
root.setAttribute("data-theme", manifest.id);
}
try {
localStorage.setItem(THEME_STORAGE_KEY, manifest.id);
if (manifest.colorSchemes === "both") {
localStorage.removeItem(THEME_SCHEME_STORAGE_KEY);
} else {
localStorage.setItem(THEME_SCHEME_STORAGE_KEY, manifest.colorSchemes);
}
localStorage.setItem(
THEME_FONTS_STORAGE_KEY,
JSON.stringify(manifest.fontLinks ?? [])
);
} catch {
// storage unavailable (private mode) — theme still applies for session
}
}, [manifest, ready]);
useEffect(() => {
if (!ready) return;
const links = Array.from(
document.querySelectorAll<HTMLLinkElement>("link[data-flxn-theme-font]")
);
const wanted = new Set(manifest.fontLinks ?? []);
const present = new Set<string>();
for (const link of links) {
const href = link.getAttribute("href");
if (!href || !wanted.has(href)) {
link.remove();
} else {
present.add(href);
}
}
for (const href of wanted) {
if (present.has(href)) continue;
const link = document.createElement("link");
link.rel = "stylesheet";
link.href = href;
link.dataset.flxnThemeFont = manifest.id;
document.head.appendChild(link);
}
}, [manifest, ready]);
return children;
};
export default ThemeProvider;
+6
View File
@@ -0,0 +1,6 @@
const themeCss = import.meta.glob("./*/theme.css", { eager: true });
// The glob result must stay used, or Rollup tree-shakes every theme.css out
// of the production bundle.
(globalThis as Record<string, unknown>).__flxnThemeCssCount =
Object.keys(themeCss).length;
+26
View File
@@ -0,0 +1,26 @@
import type { MessageDescriptor } from "@lingui/core";
import type { MantineThemeOverride } from "@mantine/core";
export type ThemeColorSchemes = "both" | "light" | "dark";
export interface ThemeChromeColors {
light?: { base: string; dimmed: string };
dark?: { base: string; dimmed: string };
}
export interface ThemeManifest {
/** Must match the theme's folder name; becomes the html[data-theme] value. */
id: string;
label: MessageDescriptor;
/** "light"/"dark" force that scheme while the theme is active. */
colorSchemes: ThemeColorSchemes;
/** Sort position in the picker; default theme is 0, unset sorts last. */
order?: number;
/** Stylesheet URLs (e.g. Google Fonts) injected while the theme is active. */
fontLinks?: string[];
/** Browser chrome theme-color meta overrides; falls back to defaults. */
chromeColors?: ThemeChromeColors;
/** JS-side Mantine override (defaultProps, component vars, variants…).
* Must never set primaryColor — the user's accent choice always applies. */
mantine?: MantineThemeOverride;
}
+136
View File
@@ -0,0 +1,136 @@
import { msg } from "@lingui/core/macro";
import type { ThemeManifest } from "../types";
/* Viewport gizmo ramps, declared in JS (not theme.css) so Mantine regenerates
every derived variant var (-filled, -light rgba, autoContrast picks) from
the retuned shades. Shade 6 is the fill in BOTH schemes (primaryShade
below) — each shade-6 hand-checked against its autoContrast on-color
(white or the custom ink black) for 4.5:1+. */
const gizmoColors = {
// Z-axis blue
blue: [
"#eaf2ff",
"#d2e4fe",
"#a8cbfd",
"#7aaffa",
"#4f92f5",
"#2f7cf0",
"#1c64d6",
"#154fb0",
"#103e8a",
"#0a2c62",
],
// X-axis red
red: [
"#ffecec",
"#ffd2d2",
"#ffa8a8",
"#fd7c7c",
"#f85454",
"#ef3535",
"#d0231f",
"#a91b19",
"#821513",
"#5c0f0e",
],
// Y-axis green
green: [
"#e7fbf0",
"#c9f5da",
"#98e8b8",
"#64d692",
"#38c072",
"#1a9e5a",
"#0e7d3f",
"#0a6432",
"#074b26",
"#05331a",
],
// measurement/annotation yellow
yellow: [
"#fff8dc",
"#ffedad",
"#ffdf76",
"#ffce46",
"#f9bc24",
"#daa00e",
"#bd870a",
"#946909",
"#6c4c07",
"#463005",
],
// rotate-gizmo violet
grape: [
"#f3edff",
"#e2d3fd",
"#c8adfa",
"#ac85f5",
"#9163ec",
"#7a47df",
"#6535c0",
"#522a9c",
"#3f2077",
"#2c1653",
],
// classic viewport selection orange
orange: [
"#fff1e3",
"#ffddb8",
"#ffc182",
"#ffa350",
"#ee8a2c",
"#cc6a0f",
"#b04f06",
"#8f4005",
"#6e3104",
"#4d2203",
],
// magenta gizmo tag
pink: [
"#ffeef7",
"#ffd3ea",
"#ffa8d6",
"#fd7cc0",
"#f552a9",
"#db308f",
"#c22174",
"#9a1a5b",
"#731442",
"#4d0d2c",
],
// chartreuse highlight
lime: [
"#f6ffe0",
"#e8feb0",
"#d3f47a",
"#bce34a",
"#a3ce27",
"#8db518",
"#7a9c12",
"#607b0d",
"#475c09",
"#2f3d06",
],
} as const;
const manifest: ThemeManifest = {
id: "wireframe",
label: msg`Viewport`,
colorSchemes: "both",
fontLinks: [
"https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap",
],
chromeColors: {
light: { base: "#f2f3f5", dimmed: "#dcdfe3" },
dark: { base: "#17191c", dimmed: "#0d0e10" },
},
mantine: {
autoContrast: true,
luminanceThreshold: 0.25,
black: "#15181c",
primaryShade: { light: 6, dark: 6 },
colors: gizmoColors,
},
};
export default manifest;
+545
View File
@@ -0,0 +1,545 @@
/* Wireframe "Viewport" — an untextured 3D-modeling viewport. Surfaces are
unshaded mesh objects: flat fills, thin vector edges, vertex dots at the
four corners. Selection follows the classic viewport convention: the
user's accent as a 1px outline + accent vertex dots + a light accent
wash. Isometric-cube divider motif; mono annotation labels (JetBrains
Mono) against clean, heavier Inter headings. */
/* ---------- 1. Tokens, any scheme ---------- */
[data-theme="wireframe"][data-theme] {
--mantine-font-family: "Inter", sans-serif;
--mantine-font-family-headings: "Inter", sans-serif;
--mantine-font-family-monospace: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
--mantine-heading-font-weight: 800;
--mantine-radius-default: 3px;
--mantine-radius-xs: 2px;
--mantine-radius-sm: 3px;
--mantine-radius-md: 4px;
--mantine-radius-lg: 4px;
--mantine-radius-xl: 6px;
--mantine-shadow-xs: none;
--mantine-shadow-sm: none;
--mantine-shadow-md: none;
--mantine-shadow-lg: none;
--mantine-shadow-xl: none;
/* square viewport avatar, vertex-dotted corners applied via .flxn-avatar-frame */
--flxn-avatar-radius: 3px;
--flxn-badge-radius: 3px;
}
/* ---------- 2. Per-scheme tokens ---------- */
[data-theme="wireframe"][data-theme][data-mantine-color-scheme="light"] {
/* thin vector edge ink + vertex-dot color */
--wf-edge: #445;
--wf-edge-dim: rgba(68, 68, 85, 0.32);
--wf-vertex: #445;
--wf-surface: #ffffff;
--wf-panel: #f8f9fa;
/* the user's accent, kept legible as ink on the mesh */
--wf-accent-ink: var(--mantine-primary-color-6);
--wf-cube-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpolygon points='12,2 20.7,7 20.7,17 12,22 3.3,17 3.3,7' fill='none' stroke='%23445' stroke-width='1.5'/%3E%3Cpath d='M12 12 L12 2 M12 12 L3.3 17 M12 12 L20.7 17' stroke='%23445' stroke-width='1.5' fill='none'/%3E%3C/svg%3E");
--flxn-avatar-border: 1px solid var(--wf-edge);
--mantine-color-body: #f2f3f5;
--mantine-color-text: #20242a;
--mantine-color-bright: #0e1013;
--mantine-color-dimmed: #55606b;
--mantine-color-anchor: var(--wf-accent-ink);
--mantine-color-error: #d0231f;
--mantine-color-placeholder: #7d8590;
--mantine-color-default: #ffffff;
--mantine-color-default-hover: #eaecef;
--mantine-color-default-color: #20242a;
--mantine-color-default-border: rgba(68, 68, 85, 0.45);
/* cool viewport gray ramp */
--mantine-color-gray-0: #f8f9fa;
--mantine-color-gray-1: #eef0f2;
--mantine-color-gray-2: #e2e5e9;
--mantine-color-gray-3: #d1d5db;
--mantine-color-gray-4: #b7bcc4;
--mantine-color-gray-5: #9aa1a8;
--mantine-color-gray-6: #7d8590;
--mantine-color-gray-7: #5f666f;
--mantine-color-gray-8: #454b52;
--mantine-color-gray-9: #2c3136;
/* app tokens */
--flxn-gold: #9a6700;
--flxn-silver: #5f666f;
--flxn-bronze: #a05a1e;
--flxn-toast-success: #0e7d3f;
--flxn-toast-error: #d0231f;
--flxn-podium-1-bg: rgba(154, 103, 0, 0.06);
--flxn-podium-1-border: rgba(154, 103, 0, 0.5);
--flxn-podium-1-label: #855800;
--flxn-podium-2-bg: rgba(95, 102, 111, 0.07);
--flxn-podium-2-border: rgba(95, 102, 111, 0.5);
--flxn-podium-2-label: #4d545c;
--flxn-podium-3-bg: rgba(160, 90, 30, 0.06);
--flxn-podium-3-border: rgba(160, 90, 30, 0.45);
--flxn-podium-3-label: #a05a1e;
--flxn-showcase-bg: #ffffff;
--flxn-overlay: rgba(15, 17, 20, 0.4);
--flxn-dotgrid-color: rgba(68, 68, 85, 0.28);
--flxn-pop-shadow: rgba(0, 0, 0, 0);
}
[data-theme="wireframe"][data-theme][data-mantine-color-scheme="dark"] {
/* pale vector edge on the night viewport */
--wf-edge: #aab2bc;
--wf-edge-dim: rgba(170, 178, 188, 0.3);
--wf-vertex: #aab2bc;
--wf-surface: #1d2024;
--wf-panel: #131518;
--wf-accent-ink: var(--mantine-primary-color-4);
--wf-cube-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpolygon points='12,2 20.7,7 20.7,17 12,22 3.3,17 3.3,7' fill='none' stroke='%23aab2bc' stroke-width='1.5'/%3E%3Cpath d='M12 12 L12 2 M12 12 L3.3 17 M12 12 L20.7 17' stroke='%23aab2bc' stroke-width='1.5' fill='none'/%3E%3C/svg%3E");
--flxn-avatar-border: 1px solid var(--wf-edge);
--mantine-color-body: #17191c;
--mantine-color-text: #e4e6e9;
--mantine-color-bright: #ffffff;
--mantine-color-dimmed: #9aa1a8;
--mantine-color-anchor: var(--wf-accent-ink);
--mantine-color-error: #ff6b6b;
--mantine-color-placeholder: #6e747b;
--mantine-color-default: #1d2024;
--mantine-color-default-hover: #232629;
--mantine-color-default-color: #e4e6e9;
--mantine-color-default-border: rgba(170, 178, 188, 0.32);
/* steel-cast dark ramp, tuned to the night viewport body */
--mantine-color-dark-0: #eef0f2;
--mantine-color-dark-1: #d6dadd;
--mantine-color-dark-2: #b0b7bd;
--mantine-color-dark-3: #868f97;
--mantine-color-dark-4: #5b636b;
--mantine-color-dark-5: #383d43;
--mantine-color-dark-6: #232629;
--mantine-color-dark-7: #17191c;
--mantine-color-dark-8: #131518;
--mantine-color-dark-9: #0d0f11;
/* app tokens */
--flxn-gold: #ffc861;
--flxn-silver: #c3cad1;
--flxn-bronze: #e0a06a;
--flxn-toast-success: #4bd08a;
--flxn-toast-error: #ff6b6b;
--flxn-podium-1-bg: rgba(255, 200, 97, 0.08);
--flxn-podium-1-border: rgba(255, 200, 97, 0.5);
--flxn-podium-1-label: #ffc861;
--flxn-podium-2-bg: rgba(195, 202, 209, 0.08);
--flxn-podium-2-border: rgba(195, 202, 209, 0.45);
--flxn-podium-2-label: #c3cad1;
--flxn-podium-3-bg: rgba(224, 160, 106, 0.08);
--flxn-podium-3-border: rgba(224, 160, 106, 0.45);
--flxn-podium-3-label: #e0a06a;
--flxn-showcase-bg: #1d2024;
--flxn-overlay: rgba(0, 0, 0, 0.6);
--flxn-dotgrid-color: rgba(170, 178, 188, 0.16);
--flxn-pop-shadow: rgba(0, 0, 0, 0);
}
/* ---------- 3. Decoration: static perspective floor-grid hint ---------- */
[data-theme="wireframe"] body::before {
content: "";
position: fixed;
left: -30vw;
right: -30vw;
bottom: -4vh;
height: 26vh;
z-index: 9998;
pointer-events: none;
opacity: 0.22;
background-image:
repeating-linear-gradient(90deg, var(--wf-edge) 0 1px, transparent 1px 64px),
repeating-linear-gradient(0deg, var(--wf-edge) 0 1px, transparent 1px 32px);
transform: perspective(280px) rotateX(62deg);
transform-origin: 50% 100%;
-webkit-mask-image: linear-gradient(to top, #000 45%, transparent 100%);
mask-image: linear-gradient(to top, #000 45%, transparent 100%);
}
/* ---------- 4. Mesh surfaces: flat fill, thin edge, vertex dots ---------- */
[data-theme="wireframe"] .mantine-Paper-root,
[data-theme="wireframe"] .mantine-Card-root,
[data-theme="wireframe"] .flxn-tile,
[data-theme="wireframe"] .flxn-match-card {
position: relative;
background-color: var(--wf-surface);
border: 1px solid var(--wf-edge-dim);
box-shadow: none;
}
[data-theme="wireframe"] .mantine-Card-root,
[data-theme="wireframe"] .flxn-tile,
[data-theme="wireframe"] .flxn-match-card {
border-color: var(--wf-edge);
}
[data-theme="wireframe"] .mantine-Card-root::after,
[data-theme="wireframe"] .flxn-tile::after,
[data-theme="wireframe"] .flxn-match-card::after {
content: "";
position: absolute;
inset: 0;
pointer-events: none;
background-repeat: no-repeat;
background-image:
radial-gradient(var(--wf-vertex) 0%, var(--wf-vertex) 100%),
radial-gradient(var(--wf-vertex) 0%, var(--wf-vertex) 100%),
radial-gradient(var(--wf-vertex) 0%, var(--wf-vertex) 100%),
radial-gradient(var(--wf-vertex) 0%, var(--wf-vertex) 100%);
background-size: 4px 4px;
background-position:
-2px -2px,
calc(100% + 2px) -2px,
-2px calc(100% + 2px),
calc(100% + 2px) calc(100% + 2px);
}
[data-theme="wireframe"] .mantine-Divider-root,
[data-theme="wireframe"] .mantine-Divider-root::before,
[data-theme="wireframe"] .mantine-Divider-root::after {
--divider-color: var(--wf-edge-dim);
}
/* isometric-cube divider motif on labeled dividers */
[data-theme="wireframe"] .mantine-Divider-label {
display: inline-flex;
align-items: center;
gap: 0.4em;
font-family: var(--mantine-font-family-monospace);
text-transform: uppercase;
letter-spacing: 0.07em;
font-size: 0.72rem;
color: var(--mantine-color-dimmed);
}
[data-theme="wireframe"] .mantine-Divider-label::before {
content: "";
display: inline-block;
width: 11px;
height: 11px;
flex: none;
background-image: var(--wf-cube-icon);
background-repeat: no-repeat;
background-size: contain;
}
/* ---------- 5. Annotation type: mono, uppercase, tracked ---------- */
[data-theme="wireframe"] .mantine-InputWrapper-label,
[data-theme="wireframe"] .mantine-Badge-label,
[data-theme="wireframe"] .mantine-Tabs-tab,
[data-theme="wireframe"] .mantine-SegmentedControl-label,
[data-theme="wireframe"] .mantine-Notification-title,
[data-theme="wireframe"] .mantine-Modal-title,
[data-theme="wireframe"] .mantine-Menu-label,
[data-theme="wireframe"] .mantine-Alert-title,
[data-theme="wireframe"] .mantine-Tooltip-tooltip {
font-family: var(--mantine-font-family-monospace);
text-transform: uppercase;
letter-spacing: 0.06em;
}
[data-theme="wireframe"] .mantine-InputWrapper-label {
font-size: 0.72rem;
font-weight: 600;
color: var(--mantine-color-dimmed);
}
/* ---------- 6. Controls: outlined technical boxes ---------- */
[data-theme="wireframe"] .mantine-Button-root {
font-family: var(--mantine-font-family-monospace);
text-transform: uppercase;
letter-spacing: 0.06em;
font-weight: 600;
box-shadow: none;
}
[data-theme="wireframe"] .mantine-Button-root[data-variant="default"],
[data-theme="wireframe"] .mantine-ActionIcon-root[data-variant="default"] {
background-color: var(--wf-surface);
border: 1px solid var(--wf-edge);
}
[data-theme="wireframe"] .mantine-Button-root[data-variant="filled"],
[data-theme="wireframe"] .mantine-ActionIcon-root[data-variant="filled"] {
border: 1px solid var(--mantine-primary-color-filled);
}
[data-theme="wireframe"] .mantine-Input-input {
background-color: var(--wf-surface);
border-color: var(--wf-edge-dim);
}
[data-theme="wireframe"] .mantine-Input-input:focus,
[data-theme="wireframe"] .mantine-Input-input:focus-within {
border-color: var(--mantine-primary-color-filled);
outline: 1px solid var(--mantine-primary-color-filled);
outline-offset: -1px;
}
[data-theme="wireframe"] .mantine-Checkbox-input {
border-color: var(--wf-edge);
background-color: var(--wf-surface);
}
[data-theme="wireframe"] .mantine-Checkbox-input:checked {
background-color: var(--mantine-primary-color-filled);
border-color: var(--mantine-primary-color-filled);
}
[data-theme="wireframe"] .mantine-Switch-track {
border-radius: 2px;
border: 1px solid var(--wf-edge);
background-color: var(--wf-surface);
}
[data-theme="wireframe"] .mantine-Switch-thumb {
border-radius: 1px;
}
[data-theme="wireframe"] .mantine-SegmentedControl-root {
background-color: var(--wf-panel);
border: 1px solid var(--wf-edge-dim);
}
[data-theme="wireframe"] .mantine-SegmentedControl-indicator {
background-color: var(--mantine-primary-color-filled);
box-shadow: none;
}
[data-theme="wireframe"] .mantine-SegmentedControl-label[data-active] {
color: var(--mantine-primary-color-contrast);
}
[data-theme="wireframe"] .mantine-Badge-root {
box-shadow: none;
}
[data-theme="wireframe"] .mantine-Badge-root[data-variant="light"],
[data-theme="wireframe"] .mantine-Badge-root[data-variant="default"],
[data-theme="wireframe"] .mantine-Badge-root[data-variant="outline"] {
border: 1px solid color-mix(in srgb, currentColor 55%, transparent);
}
[data-theme="wireframe"] .mantine-Progress-root {
border: 1px solid var(--wf-edge-dim);
background-color: var(--wf-panel);
}
[data-theme="wireframe"] .mantine-Avatar-root,
[data-theme="wireframe"] .mantine-Avatar-root .mantine-Avatar-image,
[data-theme="wireframe"] .mantine-ThemeIcon-root {
border-radius: 3px;
}
/* selection: accent 1px ring on the highlighted combobox option */
[data-theme="wireframe"] [data-combobox-selected] {
box-shadow: inset 0 0 0 1px var(--mantine-primary-color-filled);
}
/* ---------- 7. Overlays: opaque panels, no shadow ---------- */
[data-theme="wireframe"] .mantine-Modal-content {
background-color: var(--wf-surface);
border: 1px solid var(--wf-edge);
box-shadow: none;
}
[data-theme="wireframe"] .mantine-Modal-header {
background-color: var(--wf-surface);
border-bottom: 1px solid var(--wf-edge-dim);
}
[data-theme="wireframe"] .mantine-Popover-dropdown,
[data-theme="wireframe"] .mantine-Menu-dropdown,
[data-theme="wireframe"] .mantine-Combobox-dropdown {
background-color: var(--wf-surface);
border: 1px solid var(--wf-edge);
box-shadow: none;
}
[data-theme="wireframe"] .mantine-Tooltip-tooltip {
font-size: 0.68rem;
background-color: var(--wf-panel);
color: var(--mantine-color-text);
border: 1px solid var(--wf-edge);
box-shadow: none;
}
[data-theme="wireframe"] .mantine-Notification-root {
background-color: var(--wf-surface);
border: 1px solid var(--wf-edge);
box-shadow: none;
}
[data-theme="wireframe"] .mantine-Alert-root {
border: 1px solid color-mix(in srgb, currentColor 40%, transparent);
box-shadow: none;
}
/* ---------- 8. Shell ---------- */
[data-theme="wireframe"] .mantine-AppShell-root {
--app-shell-border-color: var(--wf-edge-dim);
}
[data-theme="wireframe"] .mantine-AppShell-header,
[data-theme="wireframe"] .mantine-AppShell-navbar {
background-color: var(--mantine-color-body);
border-color: var(--wf-edge-dim);
}
/* ---------- 9. Selection convention: accent outline + wash + vertex ---------- */
[data-theme="wireframe"] .mantine-NavLink-root[data-active] {
background-color: var(--mantine-primary-color-light);
color: var(--mantine-primary-color-light-color);
box-shadow: inset 0 0 0 1px var(--mantine-primary-color-filled);
}
[data-theme="wireframe"] .mantine-NavLink-root[data-active] .mantine-NavLink-label,
[data-theme="wireframe"] .mantine-NavLink-root[data-active] .mantine-NavLink-section {
color: var(--mantine-primary-color-light-color);
}
/* ---------- 10. App hooks: viewport language ---------- */
/* tab strip: resting stays flat; docked = title strip with heavy edge rule */
[data-theme="wireframe"] .flxn-tabs[data-stuck] {
border-bottom: 2px solid var(--wf-edge);
}
[data-theme="wireframe"] .flxn-tab.flxn-tab {
font-family: var(--mantine-font-family-monospace);
text-transform: uppercase;
letter-spacing: 0.06em;
font-size: 0.75rem;
color: var(--mantine-color-dimmed);
}
[data-theme="wireframe"] .flxn-tab.flxn-tab[data-active] {
color: var(--wf-accent-ink);
font-weight: 700;
}
[data-theme="wireframe"] .flxn-tabs-indicator.flxn-tabs-indicator {
border-bottom: 2px solid var(--mantine-primary-color-filled);
padding-inline: 0.5rem;
}
/* nav tiles: mesh surface (border + vertex dots from rule 4); selecting them
(hover/focus) takes on the accent selection convention */
[data-theme="wireframe"] .flxn-tile-icon {
color: var(--wf-accent-ink);
}
@media (hover: hover) {
[data-theme="wireframe"] .flxn-tile:hover {
border-color: var(--mantine-primary-color-filled);
background-color: var(--mantine-primary-color-light);
}
[data-theme="wireframe"] .flxn-tile:hover::after {
background-image:
radial-gradient(var(--mantine-primary-color-filled) 0%, var(--mantine-primary-color-filled) 100%),
radial-gradient(var(--mantine-primary-color-filled) 0%, var(--mantine-primary-color-filled) 100%),
radial-gradient(var(--mantine-primary-color-filled) 0%, var(--mantine-primary-color-filled) 100%),
radial-gradient(var(--mantine-primary-color-filled) 0%, var(--mantine-primary-color-filled) 100%);
}
/* accent icon over the light accent wash needs the wash's own on-color */
[data-theme="wireframe"] .flxn-tile:hover .flxn-tile-icon {
color: var(--mantine-primary-color-light-color);
}
}
[data-theme="wireframe"] .flxn-tile:focus-visible {
border-color: var(--mantine-primary-color-filled);
background-color: var(--mantine-primary-color-light);
outline: none;
}
[data-theme="wireframe"] .flxn-tile:focus-visible .flxn-tile-icon {
color: var(--mantine-primary-color-light-color);
}
/* "see all": annotation chip */
[data-theme="wireframe"] .flxn-see-all.flxn-see-all {
font-family: var(--mantine-font-family-monospace);
text-transform: uppercase;
letter-spacing: 0.06em;
font-size: 0.7rem;
font-weight: 600;
border: 1px solid var(--wf-edge-dim);
background-color: transparent;
color: var(--wf-accent-ink);
}
@media (hover: hover) {
[data-theme="wireframe"] .flxn-see-all.flxn-see-all:hover {
background-color: var(--mantine-primary-color-light);
border-color: var(--mantine-primary-color-filled);
color: var(--mantine-primary-color-light-color);
text-decoration: none;
}
}
/* reaction tray: properties-panel strip under the match-card mesh object */
[data-theme="wireframe"] .flxn-reaction-row.flxn-reaction-row {
background-color: var(--wf-panel);
border: 1px solid var(--wf-edge-dim);
font-family: var(--mantine-font-family-monospace);
}
/* square avatar, vertex-dotted corners just outside the frame */
[data-theme="wireframe"] .flxn-avatar-frame {
position: relative;
}
[data-theme="wireframe"] .flxn-avatar-frame::after {
content: "";
position: absolute;
inset: -2px;
pointer-events: none;
background-repeat: no-repeat;
background-image:
radial-gradient(var(--wf-vertex) 0%, var(--wf-vertex) 100%),
radial-gradient(var(--wf-vertex) 0%, var(--wf-vertex) 100%),
radial-gradient(var(--wf-vertex) 0%, var(--wf-vertex) 100%),
radial-gradient(var(--wf-vertex) 0%, var(--wf-vertex) 100%);
background-size: 4px 4px;
background-position: 0 0, 100% 0, 0 100%, 100% 100%;
}
/* press feedback: a light scale-down + accent selection-ring flash */
[data-theme="wireframe"] .flxn-press {
outline: 1px solid transparent;
outline-offset: -1px;
transition: transform 150ms cubic-bezier(0.32, 0.72, 0, 1), outline-color 150ms ease;
}
[data-theme="wireframe"] .flxn-press:active:not(:disabled):not([data-disabled]):not([data-loading]) {
transform: scale(0.97);
outline-color: var(--mantine-primary-color-filled);
}
@media (prefers-reduced-motion: reduce) {
[data-theme="wireframe"] .flxn-press,
[data-theme="wireframe"] .flxn-press:active:not(:disabled):not([data-disabled]):not([data-loading]) {
transition: none;
transform: none;
}
}
-2
View File
@@ -60,7 +60,6 @@ const verifySuperTokensSession = async (request: Request) => {
const userAuthId = session?.userId; const userAuthId = session?.userId;
if (!userAuthId || !session) { if (!userAuthId || !session) {
logger.info("No authenticated user");
return { context: { userAuthId: null, roles: [] } }; return { context: { userAuthId: null, roles: [] } };
} }
@@ -109,7 +108,6 @@ export const getSessionContextCore = async (
} }
if (!session.context.userAuthId) { if (!session.context.userAuthId) {
logger.error("Unauthenticated user", session.context);
throw new Error("Unauthenticated"); throw new Error("Unauthenticated");
} }