Current section
Files
Jump to
Current section
Files
priv/templates/theme/theme.css
/*
* PhiaUI — TailwindCSS v4 Theme
*
* CSS custom properties registered via @theme for automatic utility generation.
* All color values use the oklch() color space for wide-gamut support.
*
* Light mode is the default. Dark mode is controlled EXCLUSIVELY via the
* `.dark` class applied to <html> by the PhiaDarkMode hook. The hook also
* detects `prefers-color-scheme` on first load and persists the preference
* to localStorage.
*
* Generated utilities examples:
* bg-primary, text-foreground, border-border, ring-ring, bg-muted, etc.
*/
/* Dark mode via .dark class (PhiaDarkMode hook) */
@custom-variant dark (&:where(.dark, .dark *));
@theme {
/* ── Typography ──────────────────────────────────────────────────────────── */
--font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
--font-mono: ui-monospace, "Fira Code", "Cascadia Code", Menlo, Monaco, monospace;
--font-size-xs: 0.75rem;
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.125rem;
--font-size-xl: 1.25rem;
--font-size-2xl: 1.5rem;
--font-size-3xl: 1.875rem;
--font-size-4xl: 2.25rem;
/* ── Spacing & Shape ─────────────────────────────────────────────────────── */
--radius: 0.625rem;
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--radius-2xl: calc(var(--radius) + 8px);
/* ── Shadow Scale ────────────────────────────────────────────────────────── */
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
/* ── Semantic Color Tokens — Light Mode ──────────────────────────────────── */
/* Page surface */
--color-background: oklch(1 0 0);
--color-foreground: oklch(0.141 0.004 285.82);
/* Card surface */
--color-card: oklch(1 0 0);
--color-card-foreground: oklch(0.141 0.004 285.82);
/* Popover / dropdown surface */
--color-popover: oklch(1 0 0);
--color-popover-foreground: oklch(0.141 0.004 285.82);
/* Primary action */
--color-primary: oklch(0.211 0.005 285.82);
--color-primary-foreground: oklch(0.985 0 0);
/* Secondary / ghost */
--color-secondary: oklch(0.967 0.001 286.38);
--color-secondary-foreground: oklch(0.211 0.005 285.82);
/* Muted / disabled */
--color-muted: oklch(0.97 0 0);
--color-muted-foreground: oklch(0.552 0.016 286.07);
/* Accent / hover highlight */
--color-accent: oklch(0.967 0.001 286.38);
--color-accent-foreground: oklch(0.211 0.005 285.82);
/* Destructive / error */
--color-destructive: oklch(0.577 0.245 27.325);
--color-destructive-foreground: oklch(0.985 0 0);
/* Structural */
--color-border: oklch(0.922 0 0);
--color-input: oklch(0.922 0 0);
--color-ring: oklch(0.141 0.004 285.82);
/* ── Dashboard Shell — Light Mode ────────────────────────────────────────── */
--color-sidebar-background: oklch(0.985 0.001 286.38);
--color-sidebar-foreground: oklch(0.141 0.004 285.82);
--color-sidebar-primary: oklch(0.211 0.005 285.82);
--color-sidebar-primary-foreground: oklch(0.985 0 0);
--color-sidebar-accent: oklch(0.967 0.001 286.38);
--color-sidebar-accent-foreground: oklch(0.211 0.005 285.82);
--color-sidebar-border: oklch(0.922 0 0);
/* ── Animation Tokens ───────────────────────────────────────────────────── */
--animate-marquee: marquee 40s linear infinite;
--animate-marquee-vertical: marquee-vertical 20s linear infinite;
--animate-orbit: orbit 10s linear infinite;
--animate-aurora: aurora 8s ease infinite;
--animate-meteor: meteor 5s linear infinite;
--animate-float: float 3s ease-in-out infinite;
--animate-pulse-ring: pulse-ring 1.5s cubic-bezier(0, 0, 0.2, 1) infinite;
--animate-shiny-text: shiny-text 8s ease-in-out infinite;
--animate-ripple-wave: ripple-wave 2s ease-out infinite;
--animate-typing-bounce: typing-bounce 1.2s ease-in-out infinite;
--animate-wave-bar: wave-bar 1.2s ease-in-out infinite;
--animate-blink-cursor: blink-cursor 1s step-end infinite;
--animate-border-spin: border-spin 4s linear infinite;
}
/* ── CSS Variable Aliases ─────────────────────────────────────────────────── */
/*
* Short-form aliases for direct CSS custom property references in components
* (e.g. bg-[--background], bg-[--sidebar-background]).
*/
:root {
--background: var(--color-background);
--sidebar-background: var(--color-sidebar-background);
}
/* ── Dark Mode Overrides — controlled by .dark class on <html> ────────────── */
/*
* PhiaDarkMode hook applies .dark to <html> based on:
* 1. localStorage['phia-theme'] (persisted preference)
* 2. prefers-color-scheme (system default on first visit)
*
* Anti-FOUC: add this inline script to <head> BEFORE any stylesheet:
*
* <script>
* (function() {
* var t = localStorage.getItem('phia-theme');
* if (t === 'dark' || (!t && matchMedia('(prefers-color-scheme: dark)').matches)) {
* document.documentElement.classList.add('dark');
* }
* })();
* </script>
*/
.dark {
--color-background: oklch(0.141 0.004 285.82);
--color-foreground: oklch(0.985 0 0);
--color-card: oklch(0.211 0.005 285.82);
--color-card-foreground: oklch(0.985 0 0);
--color-popover: oklch(0.211 0.005 285.82);
--color-popover-foreground: oklch(0.985 0 0);
--color-primary: oklch(0.985 0 0);
--color-primary-foreground: oklch(0.211 0.005 285.82);
--color-secondary: oklch(0.279 0.007 285.82);
--color-secondary-foreground: oklch(0.985 0 0);
--color-muted: oklch(0.279 0.007 285.82);
--color-muted-foreground: oklch(0.71 0.01 286.07);
--color-accent: oklch(0.279 0.007 285.82);
--color-accent-foreground: oklch(0.985 0 0);
--color-destructive: oklch(0.704 0.191 22.216);
--color-destructive-foreground: oklch(0.985 0 0);
--color-border: oklch(1 0 0 / 10%);
--color-input: oklch(1 0 0 / 15%);
--color-ring: oklch(0.552 0.016 286.07);
--color-sidebar-background: oklch(0.211 0.005 285.82);
--color-sidebar-foreground: oklch(0.985 0 0);
--color-sidebar-primary: oklch(0.985 0 0);
--color-sidebar-primary-foreground: oklch(0.211 0.005 285.82);
--color-sidebar-accent: oklch(0.279 0.007 285.82);
--color-sidebar-accent-foreground: oklch(0.985 0 0);
--color-sidebar-border: oklch(1 0 0 / 10%);
}
/* ── Animation Keyframes ─────────────────────────────────────────────────── */
@keyframes marquee {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
@keyframes marquee-vertical {
from { transform: translateY(0); }
to { transform: translateY(-50%); }
}
@keyframes orbit {
from { transform: rotate(0deg) translateX(var(--orbit-radius, 60px)) rotate(0deg); }
to { transform: rotate(360deg) translateX(var(--orbit-radius, 60px)) rotate(-360deg); }
}
@keyframes aurora {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
@keyframes meteor {
0% { transform: rotate(215deg) translateX(0); opacity: 1; }
70% { opacity: 1; }
100% { transform: rotate(215deg) translateX(-500px); opacity: 0; }
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(var(--float-y, -10px)); }
}
@keyframes pulse-ring {
0% { transform: scale(1); opacity: 0.8; }
100% { transform: scale(2.5); opacity: 0; }
}
@keyframes shiny-text {
0%, 90%, 100% { background-position: calc(-100% - var(--shiny-width, 40%)) 0; }
30%, 60% { background-position: calc(100% + var(--shiny-width, 40%)) 0; }
}
@keyframes ripple-wave {
0% { transform: scale(1); opacity: 0.6; }
100% { transform: scale(3); opacity: 0; }
}
@keyframes typing-bounce {
0%, 60%, 100% { transform: translateY(0); }
30% { transform: translateY(-6px); }
}
@keyframes wave-bar {
0%, 40%, 100% { transform: scaleY(0.4); }
20% { transform: scaleY(1); }
}
@keyframes blink-cursor {
0%, 49% { opacity: 1; }
50%, 100% { opacity: 0; }
}
@keyframes border-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* ── Chart Animation Keyframes ───────────────────────────────────────────── */
@keyframes phia-bar-grow {
from { transform: scaleY(0); }
to { transform: scaleY(1); }
}
@keyframes phia-bar-grow-x {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
@keyframes phia-line-draw {
from { stroke-dashoffset: var(--phia-line-len, 1000); }
to { stroke-dashoffset: 0; }
}
@keyframes phia-dot-pop {
from { transform: scale(0.2); opacity: 0; }
to { transform: scale(1); opacity: 1; }
}
@keyframes phia-fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes phia-gauge-fill {
from { stroke-dashoffset: var(--phia-gauge-len, 141); }
to { stroke-dashoffset: 0; }
}
/* ── Drag-and-Drop Keyframes ──────────────────────────────────────────────── */
@keyframes phia-drop-in {
0% { transform: scale(0.95) translateY(4px); opacity: 0; }
60% { transform: scale(1.02) translateY(-2px); opacity: 1; }
100% { transform: scale(1) translateY(0); opacity: 1; }
}
@keyframes phia-drag-pulse {
0%, 100% {
border-color: oklch(var(--color-primary) / 40%);
background-color: oklch(var(--color-primary) / 5%);
}
50% {
border-color: oklch(var(--color-primary) / 80%);
background-color: oklch(var(--color-primary) / 12%);
}
}
/* ── Drag-and-Drop Utilities ──────────────────────────────────────────────── */
.phia-dragging {
opacity: 0.4;
box-shadow: var(--shadow-lg);
pointer-events: none;
}
.phia-drag-over {
border-color: oklch(var(--color-primary));
background-color: oklch(var(--color-primary) / 8%);
}
/* Disable all chart animations when user prefers reduced motion */
@media (prefers-reduced-motion: reduce) {
.phia-chart-animate,
.phia-chart-animate * {
animation: none !important;
transition: none !important;
}
[data-dragging],
[data-drag-over] {
animation: none !important;
transition: none !important;
}
}
/* ── Editor Suite ─────────────────────────────────────────────────────────── */
@keyframes phia-bubble-in {
from { opacity: 0; transform: translateY(4px) scale(0.97); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes phia-slash-in {
from { opacity: 0; transform: translateY(-4px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes phia-find-slide {
from { opacity: 0; max-height: 0; }
to { opacity: 1; max-height: 60px; }
}
@keyframes phia-dropdown-in {
from { opacity: 0; transform: scaleY(0.95); transform-origin: top; }
to { opacity: 1; transform: scaleY(1); }
}
.phia-bubble-menu-open { animation: phia-bubble-in 120ms ease-out forwards; }
.phia-slash-menu-open { animation: phia-slash-in 100ms ease-out forwards; }
.phia-find-bar-open { animation: phia-find-slide 150ms ease-out forwards; }
/* Active swatch indicator — ring around currently selected color */
.phia-swatch-active {
outline: 2px solid var(--color-ring);
outline-offset: 2px;
}
/* Find/replace mark highlight */
.phia-find-mark {
border-radius: 2px;
}
/* ── Base Layer ───────────────────────────────────────────────────────────── */
@layer base {
* {
border-color: var(--color-border);
}
body {
background-color: var(--color-background);
color: var(--color-foreground);
font-family: var(--font-sans);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
}