Packages

shadcn/ui-inspired component library for Phoenix LiveView with eject-based distribution. CSS-first theme system, 829 components across 20+ categories — Calendar, Chart, Animation, DnD, Editor, Collaboration, Typography, Navigation, Background, Surface, and more.

Current section

Files

Jump to
phia_ui priv templates theme theme.css
Raw

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;
/* ── Fluid Typography Scale ───────────────────────────────────────────── */
--font-size-fluid-xs: clamp(0.625rem, 0.5rem + 0.5vw, 0.75rem);
--font-size-fluid-sm: clamp(0.75rem, 0.625rem + 0.5vw, 0.875rem);
--font-size-fluid-base: clamp(0.875rem, 0.75rem + 0.5vw, 1rem);
--font-size-fluid-lg: clamp(1rem, 0.875rem + 0.625vw, 1.25rem);
--font-size-fluid-xl: clamp(1.125rem, 0.875rem + 1vw, 1.5rem);
--font-size-fluid-2xl: clamp(1.25rem, 0.875rem + 1.5vw, 2rem);
--font-size-fluid-3xl: clamp(1.5rem, 1rem + 2vw, 2.5rem);
--font-size-fluid-4xl: clamp(1.75rem, 1rem + 3vw, 3rem);
/* ── Fluid Spacing Scale ───────────────────────────────────────────────── */
--spacing-fluid-xs: clamp(0.25rem, 0.125rem + 0.5vw, 0.5rem);
--spacing-fluid-sm: clamp(0.5rem, 0.25rem + 0.75vw, 1rem);
--spacing-fluid-md: clamp(0.75rem, 0.5rem + 1vw, 1.5rem);
--spacing-fluid-lg: clamp(1rem, 0.5rem + 1.5vw, 2rem);
--spacing-fluid-xl: clamp(1.5rem, 0.75rem + 2vw, 3rem);
--spacing-fluid-2xl: clamp(2rem, 1rem + 3vw, 4rem);
/* ── Button Wave Animation Tokens ──────────────────────────────────────── */
--animate-shimmer-pass: phia-shimmer-pass 2s ease-in-out infinite;
--animate-glow-pulse: phia-neon-pulse 2s ease-in-out infinite;
/* ── Navigation Wave Animation Tokens ──────────────────────────────────── */
--animate-command-in: phia-command-in 150ms ease-out forwards;
--animate-step-fill: phia-step-fill 300ms ease-out forwards;
--animate-toc-active: phia-toc-active 150ms ease-out forwards;
/* ── Collab Suite Animation Tokens ──────────────────────────────────────── */
--animate-cursor-blink: phia-cursor-blink 1s step-end infinite;
--animate-typing-dots: phia-typing-dots 1.4s ease-in-out infinite;
--animate-thread-slide: phia-thread-slide 200ms ease-out forwards;
--animate-comment-pin-pop: phia-comment-pin-pop 200ms ease-out forwards;
--animate-inbox-slide: phia-inbox-slide 250ms ease-out forwards;
--animate-diff-highlight: phia-diff-highlight 2s ease-in-out;
}
/* ── 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; }
}
/* ── Advanced Chart Keyframes (v0.1.17) ────────────────────────────────────── */
@keyframes phia-candle-grow {
from { transform: scaleY(0); }
to { transform: scaleY(1); }
}
@keyframes phia-box-pop {
from { transform: scale(0.8); opacity: 0; }
to { transform: scale(1); opacity: 1; }
}
@keyframes phia-violin-draw {
from { clip-path: inset(0 100% 0 0); }
to { clip-path: inset(0); }
}
@keyframes phia-sunburst-arc {
from { transform: scale(0.9); opacity: 0; }
to { transform: scale(1); opacity: 1; }
}
@keyframes phia-sankey-flow {
from { opacity: 0; }
to { opacity: 0.5; }
}
@keyframes phia-waffle-fill {
from { transform: scale(0); }
to { transform: scale(1); }
}
@keyframes phia-stream-wave {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes phia-chord-ribbon {
from { opacity: 0; }
to { opacity: 0.65; }
}
@keyframes phia-pack-pop {
from { r: 0; }
to { r: var(--target-r); }
}
/* ── 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;
}
/* ── Surface Wave Tokens ─────────────────────────────────────────────────── */
--shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25);
/* Animated surface @property (for conic-gradient angle) */
@property --angle {
syntax: '<number>';
inherits: false;
initial-value: 0;
}
/* ── Surface Wave Keyframes ──────────────────────────────────────────────── */
@keyframes phia-border-beam {
0% { --angle: 0; }
100% { --angle: 1; }
}
@keyframes phia-shine-border {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes phia-moving-border {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
@keyframes phia-neon-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
@keyframes phia-sheet-up {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}
/* ── Button Wave Keyframes ───────────────────────────────────────────────── */
@keyframes phia-shimmer-pass {
from { transform: translateX(-100%) skewX(-15deg); }
to { transform: translateX(200%) skewX(-15deg); }
}
/* ── Navigation Wave (v0.1.10) Keyframes ────────────────────────────────── */
@keyframes phia-command-in {
from { opacity: 0; transform: scale(0.95) translateY(-4px); }
to { opacity: 1; transform: scale(1) translateY(0); }
}
@keyframes phia-step-fill {
from { width: 0%; }
to { width: 100%; }
}
@keyframes phia-toc-active {
from { opacity: 0; transform: translateX(-4px); }
to { opacity: 1; transform: translateX(0); }
}
/* ── Background Wave (v0.1.11) ────────────────────────────────────────────── */
@property --phia-conic-angle {
syntax: '<angle>';
inherits: false;
initial-value: 0deg;
}
@keyframes phia-wave-scroll {
0% { transform: translateX(0); }
100% { transform: translateX(-50%); }
}
@keyframes phia-beam-sweep {
0% { transform: translateX(-100%); opacity: 0; }
20% { opacity: 1; }
80% { opacity: 1; }
100% { transform: translateX(200%); opacity: 0; }
}
@keyframes phia-bokeh-drift {
0% { transform: translate(0,0) scale(1); }
33% { transform: translate(var(--bx, 20px), var(--by, -15px)) scale(1.05); }
66% { transform: translate(var(--bx2, -10px), var(--by2, 20px)) scale(.95); }
100% { transform: translate(0,0) scale(1); }
}
@keyframes phia-conic-spin {
from { --phia-conic-angle: 0deg; }
to { --phia-conic-angle: 360deg; }
}
@theme {
--animate-wave-scroll: phia-wave-scroll 8s linear infinite;
--animate-beam-sweep: phia-beam-sweep 4s ease-in-out infinite;
--animate-bokeh-drift: phia-bokeh-drift 8s ease-in-out infinite;
--animate-conic-spin: phia-conic-spin 6s linear infinite;
}
/* Textarea Enhancement Utilities */
.phia-code-textarea-wrap {
position: relative;
display: flex;
}
.phia-line-numbers {
position: absolute;
left: 0;
top: 0;
bottom: 0;
pointer-events: none;
user-select: none;
overflow: hidden;
}
/* ── eCharts Architecture — MarkPoint / DataZoom ─────────────────────────── */
@keyframes phia-mark-bounce {
0% { opacity: 0; transform: scale(0) translateY(4px); }
60% { opacity: 1; transform: scale(1.15) translateY(-2px); }
100% { opacity: 1; transform: scale(1) translateY(0); }
}
@keyframes phia-zoom-handle {
0%, 100% { opacity: 0.6; }
50% { opacity: 1; }
}
/* Highcharts-inspired chart animations */
@keyframes phia-crosshair-show {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes phia-drill-in {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
@keyframes phia-drill-out {
from { opacity: 1; transform: scale(1); }
to { opacity: 0; transform: scale(1.05); }
}
@keyframes phia-error-bar-pop {
from { opacity: 0; transform: scaleY(0); }
to { opacity: 1; transform: scaleY(1); }
}
.phia-crosshair-line {
pointer-events: none;
stroke-dasharray: 4 2;
}
.phia-drilldown-enter {
animation: phia-drill-in 300ms ease-out;
}
/* ── Recharts-Inspired Active Shape ──────────────────────────────────────── */
@keyframes phia-active-shape-grow {
from { transform: scale(1); }
to { transform: scale(1.08); }
}
@media (prefers-reduced-motion: reduce) {
.phia-mesh-bg *,
.phia-wave-bg *,
.phia-bokeh-bg * {
animation: none !important;
}
}
/* ── Responsive Foundation ────────────────────────────────────────────────── */
/* Touch targets on coarse pointers (phones/tablets) — WCAG 2.5.8 */
@media (pointer: coarse) {
.phia-touch-target { min-height: 44px; min-width: 44px; }
.phia-touch-padding { padding-block: 0.75rem; }
}
/* High contrast mode — thicker borders and rings */
@media (prefers-contrast: more) {
:root { --color-border: oklch(0 0 0); --color-ring: oklch(0 0 0); }
.dark { --color-border: oklch(1 0 0); --color-ring: oklch(1 0 0); }
}
/* Forced colors (Windows High Contrast) — system-aware focus and borders */
@media (forced-colors: active) {
.phia-focus-ring { outline: 2px solid ButtonText; outline-offset: 2px; }
.phia-border-semantic { border-color: ButtonText; }
}
/* Modern viewport height utilities (handles iOS Safari address bar) */
.phia-dvh { height: 100dvh; }
.phia-svh { height: 100svh; }
/* ── Lightbox ────────────────────────────────────────────────────────────── */
@keyframes phia-lightbox-in {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
@keyframes phia-lightbox-slide {
from { opacity: 0; transform: translateX(8px); }
to { opacity: 1; transform: translateX(0); }
}
/* ── TipTap / ProseMirror Editor Styles ───────────────────────────────────── */
/* ProseMirror core — remove default outline, inherit min-height from parent */
.ProseMirror {
outline: none;
min-height: inherit;
}
/* Placeholder text for empty paragraphs (TipTap Placeholder extension) */
.ProseMirror p.is-editor-empty:first-child::before {
color: var(--color-muted-foreground);
content: attr(data-placeholder);
float: left;
height: 0;
pointer-events: none;
}
/* Collaboration cursor — colored caret line */
.collaboration-cursor__caret {
border-left: 2px solid currentColor;
border-right: none;
margin-left: -1px;
margin-right: -1px;
pointer-events: none;
position: relative;
word-break: normal;
}
/* Collaboration cursor — name label above caret */
.collaboration-cursor__label {
border-radius: 3px 3px 3px 0;
color: white;
font-size: 0.625rem;
font-weight: 600;
left: -1px;
line-height: 1;
padding: 1px 4px;
position: absolute;
top: -1.4em;
user-select: none;
white-space: nowrap;
}
/* TipTap mention node styling */
.tiptap-mention {
background-color: var(--color-primary-100, oklch(0.93 0.03 264));
border-radius: 0.25rem;
color: var(--color-primary);
font-weight: 500;
padding: 0 0.25rem;
}
/* ── Collab Suite Keyframes (v0.1.18) ─────────────────────────────────────── */
@keyframes phia-cursor-blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
@keyframes phia-typing-dots {
0%, 80%, 100% { transform: translateY(0); }
40% { transform: translateY(-4px); }
}
@keyframes phia-thread-slide {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
@keyframes phia-comment-pin-pop {
from { transform: scale(0); }
to { transform: scale(1); }
}
@keyframes phia-inbox-slide {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}
@keyframes phia-diff-highlight {
0%, 100% { background-color: transparent; }
50% { background-color: oklch(0.85 0.1 90 / 0.3); }
}
/* Collab utility classes */
.phia-cursor-idle {
opacity: 0.3;
transition: opacity 0.5s ease;
}
.phia-typing-dot {
display: inline-block;
animation: phia-typing-dots 1.4s ease-in-out infinite;
}
.phia-typing-dot:nth-child(2) { animation-delay: 0.16s; }
.phia-typing-dot:nth-child(3) { animation-delay: 0.32s; }
@media (prefers-reduced-motion: reduce) {
.phia-cursor-idle { transition: none; }
.phia-typing-dot { animation: none; }
}
/* ── Editor v2 Suite ─────────────────────────────────────────────────────── */
/* A4 Page Styles */
.phia-a4-page {
width: 210mm;
max-width: 100%;
min-height: 297mm;
background: var(--color-background);
box-shadow: 0 1px 3px rgb(0 0 0 / 0.1), 0 1px 2px rgb(0 0 0 / 0.06);
padding: 25.4mm;
margin: 0 auto 2rem;
position: relative;
}
@media print {
.phia-a4-page {
box-shadow: none;
margin: 0;
padding: 25.4mm;
page-break-after: always;
break-after: page;
}
.phia-a4-page:last-child {
page-break-after: auto;
break-after: auto;
}
}
/* Syntax Highlighting Theme */
.phia-hl-keyword { color: oklch(0.55 0.18 280); } /* purple */
.phia-hl-string { color: oklch(0.55 0.15 145); } /* green */
.phia-hl-comment { color: oklch(0.6 0 0); font-style: italic; } /* gray */
.phia-hl-number { color: oklch(0.6 0.16 45); } /* orange */
.phia-hl-function { color: oklch(0.5 0.18 250); } /* blue */
.phia-hl-builtin { color: oklch(0.55 0.14 200); } /* teal */
.phia-hl-operator { color: oklch(0.5 0.12 350); } /* pink */
.phia-hl-punctuation { color: oklch(0.55 0 0); } /* gray */
.phia-hl-tag { color: oklch(0.55 0.18 20); } /* red */
.phia-hl-attribute { color: oklch(0.55 0.16 70); } /* amber */
.phia-hl-selector { color: oklch(0.55 0.18 280); }
.phia-hl-property { color: oklch(0.5 0.18 250); }
.phia-hl-decorator { color: oklch(0.55 0.16 70); }
.phia-hl-atom { color: oklch(0.55 0.18 250); }
.phia-hl-module { color: oklch(0.55 0.14 200); font-weight: 600; }
.dark .phia-hl-keyword { color: oklch(0.75 0.18 280); }
.dark .phia-hl-string { color: oklch(0.75 0.15 145); }
.dark .phia-hl-comment { color: oklch(0.55 0 0); }
.dark .phia-hl-number { color: oklch(0.75 0.16 45); }
.dark .phia-hl-function { color: oklch(0.75 0.18 250); }
.dark .phia-hl-builtin { color: oklch(0.7 0.14 200); }
.dark .phia-hl-operator { color: oklch(0.7 0.12 350); }
.dark .phia-hl-punctuation { color: oklch(0.6 0 0); }
.dark .phia-hl-tag { color: oklch(0.75 0.18 20); }
.dark .phia-hl-attribute { color: oklch(0.75 0.16 70); }
.dark .phia-hl-selector { color: oklch(0.75 0.18 280); }
.dark .phia-hl-property { color: oklch(0.75 0.18 250); }
.dark .phia-hl-decorator { color: oklch(0.75 0.16 70); }
.dark .phia-hl-atom { color: oklch(0.75 0.18 250); }
.dark .phia-hl-module { color: oklch(0.7 0.14 200); }
/* Track Changes Decorations */
.phia-tc-ins {
background: oklch(0.95 0.05 145);
border-bottom: 2px solid oklch(0.55 0.15 145);
text-decoration: none;
}
.phia-tc-del {
background: oklch(0.95 0.05 25);
text-decoration: line-through;
color: oklch(0.55 0.15 25);
opacity: 0.7;
}
.dark .phia-tc-ins {
background: oklch(0.25 0.05 145);
border-bottom-color: oklch(0.65 0.15 145);
}
.dark .phia-tc-del {
background: oklch(0.25 0.05 25);
color: oklch(0.7 0.15 25);
}
/* Equation Display */
.phia-eq-display {
font-family: var(--font-mono);
font-style: italic;
font-size: 1.125rem;
line-height: 1.8;
}
.phia-eq-frac {
display: inline-flex;
flex-direction: column;
align-items: center;
vertical-align: middle;
margin: 0 0.15em;
}
.phia-eq-frac-num { font-size: 0.85em; }
.phia-eq-frac-bar { width: 100%; height: 1px; background: currentColor; margin: 1px 0; }
.phia-eq-frac-den { font-size: 0.85em; }
.phia-eq-sqrt { display: inline-flex; align-items: stretch; }
.phia-eq-sqrt-sign { font-size: 1.2em; margin-right: 1px; }
.phia-eq-sqrt-content {
border-top: 1px solid currentColor;
padding: 0 2px;
}
.phia-eq-sup { font-size: 0.7em; vertical-align: super; }
.phia-eq-sub { font-size: 0.7em; vertical-align: sub; }
.phia-eq-symbol { font-style: normal; }
/* Grammar Underlines */
.phia-grammar-error { text-decoration: wavy underline oklch(0.55 0.2 25) 1px; text-underline-offset: 3px; }
.phia-grammar-warning { text-decoration: wavy underline oklch(0.6 0.16 80) 1px; text-underline-offset: 3px; }
.phia-grammar-info { text-decoration: wavy underline oklch(0.55 0.18 250) 1px; text-underline-offset: 3px; }
/* RTL Spacing */
[dir="rtl"] .phia-editor-toolbar { direction: rtl; }
[dir="rtl"] .phia-editor-content { text-align: right; }
/* Block Selection */
.phia-block-selected {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
border-radius: var(--radius-sm);
}
/* Cell Selection */
.phia-cell-selected {
background: oklch(0.93 0.06 250) !important;
}
.dark .phia-cell-selected {
background: oklch(0.25 0.06 250) !important;
}
/* Editor v2 Keyframes */
@keyframes phia-block-appear {
from { opacity: 0; transform: translateY(4px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes phia-tab-switch {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes phia-resize-pulse {
0%, 100% { outline-color: var(--color-primary); }
50% { outline-color: transparent; }
}
/* Print Stylesheet Additions */
@media print {
.phia-no-print { display: none !important; }
.phia-print-only { display: block !important; }
.phia-tc-ins { border-bottom: none; background: none; }
.phia-tc-del { display: none; }
}
@media (prefers-reduced-motion: reduce) {
.phia-a4-page,
.phia-block-selected,
.phia-cell-selected {
animation: none !important;
transition: none !important;
}
}
/* ── 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;
}
}