mirror of
https://github.com/layer-systems/website.git
synced 2026-09-13 06:07:29 +02:00
Raising a window made it visibly jump to the top-left corner and animate back. Two things combined: the enter keyframes animated `transform`, which overrode the inline `translate3d` a window's position is applied with, and the window layer rendered the z-sorted list, so focusing a window moved its DOM node and made the browser replay that animation. Animate the standalone `scale` property instead — it composes with the inline transform rather than replacing it — scope the animation to `.os-window` (it was hitting every child of the layer, including the snap preview, which is positioned the same way), and render windows in their stable creation order, since the inline `z-index` already handles stacking. Fixes #13 Claude-Session: https://claude.ai/code/session_01Trku191Ww2a2YmDWQF3fTS Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
351 lines
8.8 KiB
CSS
351 lines
8.8 KiB
CSS
@import "tailwindcss";
|
|
@import "tw-animate-css";
|
|
|
|
@custom-variant dark (&:is(.dark *));
|
|
|
|
@theme inline {
|
|
--color-background: var(--background);
|
|
--color-foreground: var(--foreground);
|
|
|
|
--color-card: var(--card);
|
|
--color-card-foreground: var(--card-foreground);
|
|
|
|
--color-popover: var(--popover);
|
|
--color-popover-foreground: var(--popover-foreground);
|
|
|
|
--color-primary: var(--primary);
|
|
--color-primary-foreground: var(--primary-foreground);
|
|
|
|
--color-secondary: var(--secondary);
|
|
--color-secondary-foreground: var(--secondary-foreground);
|
|
|
|
--color-muted: var(--muted);
|
|
--color-muted-foreground: var(--muted-foreground);
|
|
|
|
--color-accent: var(--accent);
|
|
--color-accent-foreground: var(--accent-foreground);
|
|
|
|
--color-destructive: var(--destructive);
|
|
--color-destructive-foreground: var(--destructive-foreground);
|
|
|
|
--color-border: var(--border);
|
|
--color-input: var(--input);
|
|
--color-ring: var(--ring);
|
|
|
|
--color-chart-1: var(--chart-1);
|
|
--color-chart-2: var(--chart-2);
|
|
--color-chart-3: var(--chart-3);
|
|
--color-chart-4: var(--chart-4);
|
|
--color-chart-5: var(--chart-5);
|
|
|
|
--color-os-desktop: var(--os-desktop);
|
|
--color-os-titlebar: var(--os-titlebar);
|
|
--color-os-titlebar-inactive: var(--os-titlebar-inactive);
|
|
--color-os-window-border: var(--os-window-border);
|
|
--color-success: var(--success);
|
|
--color-warning: var(--warning);
|
|
|
|
--color-sidebar: var(--sidebar);
|
|
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
--color-sidebar-primary: var(--sidebar-primary);
|
|
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
--color-sidebar-accent: var(--sidebar-accent);
|
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
--color-sidebar-border: var(--sidebar-border);
|
|
--color-sidebar-ring: var(--sidebar-ring);
|
|
|
|
--radius-lg: var(--radius);
|
|
--radius-md: calc(var(--radius) - 2px);
|
|
--radius-sm: calc(var(--radius) - 4px);
|
|
--radius-xl: calc(var(--radius) + 4px);
|
|
|
|
--animate-accordion-down: accordion-down 0.2s ease-out;
|
|
--animate-accordion-up: accordion-up 0.2s ease-out;
|
|
|
|
@keyframes accordion-down {
|
|
from {
|
|
height: 0;
|
|
}
|
|
to {
|
|
height: var(--radix-accordion-content-height);
|
|
}
|
|
}
|
|
|
|
@keyframes accordion-up {
|
|
from {
|
|
height: var(--radix-accordion-content-height);
|
|
}
|
|
to {
|
|
height: 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Container utility replacement for v4 (previously configured in tailwind.config) */
|
|
@utility container {
|
|
margin-inline: auto;
|
|
padding-inline: 2rem;
|
|
@media (width >= 1400px) {
|
|
max-width: 1400px;
|
|
}
|
|
}
|
|
|
|
:root {
|
|
--radius: 0.75rem;
|
|
|
|
/* Desktop surface: a warm off-white the windows can sit on without glare. */
|
|
--os-desktop: hsl(40 22% 95%);
|
|
--os-desktop-dot: hsl(30 12% 82%);
|
|
--os-menubar: hsl(40 25% 99% / 0.72);
|
|
--os-titlebar: hsl(40 20% 98%);
|
|
--os-titlebar-inactive: hsl(40 12% 96%);
|
|
--os-window-border: hsl(30 12% 86%);
|
|
--os-shadow-focused: 0 24px 60px -12px hsl(265 30% 20% / 0.22), 0 8px 20px -8px hsl(265 30% 20% / 0.14);
|
|
--os-shadow-idle: 0 8px 24px -10px hsl(265 20% 20% / 0.14);
|
|
|
|
--background: hsl(0 0% 100%);
|
|
--foreground: hsl(20 14% 12%);
|
|
|
|
--card: hsl(0 0% 100%);
|
|
--card-foreground: hsl(20 14% 12%);
|
|
|
|
--popover: hsl(0 0% 100%);
|
|
--popover-foreground: hsl(20 14% 12%);
|
|
|
|
--primary: hsl(265 85% 60%);
|
|
--primary-foreground: hsl(0 0% 100%);
|
|
|
|
--secondary: hsl(30 16% 95%);
|
|
--secondary-foreground: hsl(20 14% 18%);
|
|
|
|
--muted: hsl(30 16% 95%);
|
|
--muted-foreground: hsl(25 8% 45%);
|
|
|
|
--accent: hsl(265 60% 96%);
|
|
--accent-foreground: hsl(265 60% 30%);
|
|
|
|
--destructive: hsl(0 72% 51%);
|
|
--destructive-foreground: hsl(0 0% 100%);
|
|
|
|
--success: hsl(150 60% 38%);
|
|
--warning: hsl(38 92% 48%);
|
|
|
|
--border: hsl(30 12% 88%);
|
|
--input: hsl(30 12% 88%);
|
|
--ring: hsl(265 85% 60%);
|
|
|
|
--chart-1: hsl(265 85% 60%);
|
|
--chart-2: hsl(190 70% 45%);
|
|
--chart-3: hsl(20 90% 58%);
|
|
--chart-4: hsl(150 55% 42%);
|
|
--chart-5: hsl(330 75% 60%);
|
|
|
|
--sidebar: hsl(40 20% 97%);
|
|
--sidebar-foreground: hsl(20 12% 26%);
|
|
--sidebar-primary: hsl(265 85% 60%);
|
|
--sidebar-primary-foreground: hsl(0 0% 100%);
|
|
--sidebar-accent: hsl(30 16% 93%);
|
|
--sidebar-accent-foreground: hsl(20 14% 18%);
|
|
--sidebar-border: hsl(30 12% 88%);
|
|
--sidebar-ring: hsl(265 85% 60%);
|
|
}
|
|
|
|
.dark {
|
|
--os-desktop: hsl(265 12% 8%);
|
|
--os-desktop-dot: hsl(265 8% 20%);
|
|
--os-menubar: hsl(265 12% 12% / 0.72);
|
|
--os-titlebar: hsl(265 10% 16%);
|
|
--os-titlebar-inactive: hsl(265 10% 13%);
|
|
--os-window-border: hsl(265 8% 24%);
|
|
--os-shadow-focused: 0 24px 60px -12px hsl(265 40% 2% / 0.7), 0 8px 20px -8px hsl(265 40% 2% / 0.5);
|
|
--os-shadow-idle: 0 8px 24px -10px hsl(265 40% 2% / 0.5);
|
|
|
|
--background: hsl(265 10% 12%);
|
|
--foreground: hsl(40 12% 94%);
|
|
|
|
--card: hsl(265 10% 12%);
|
|
--card-foreground: hsl(40 12% 94%);
|
|
|
|
--popover: hsl(265 10% 14%);
|
|
--popover-foreground: hsl(40 12% 94%);
|
|
|
|
--primary: hsl(265 85% 70%);
|
|
--primary-foreground: hsl(265 40% 12%);
|
|
|
|
--secondary: hsl(265 8% 19%);
|
|
--secondary-foreground: hsl(40 12% 94%);
|
|
|
|
--muted: hsl(265 8% 19%);
|
|
--muted-foreground: hsl(265 6% 64%);
|
|
|
|
--accent: hsl(265 30% 22%);
|
|
--accent-foreground: hsl(265 60% 88%);
|
|
|
|
--destructive: hsl(0 65% 55%);
|
|
--destructive-foreground: hsl(0 0% 100%);
|
|
|
|
--success: hsl(150 55% 50%);
|
|
--warning: hsl(38 90% 58%);
|
|
|
|
--border: hsl(265 8% 22%);
|
|
--input: hsl(265 8% 22%);
|
|
--ring: hsl(265 85% 70%);
|
|
|
|
--chart-1: hsl(265 85% 70%);
|
|
--chart-2: hsl(190 70% 58%);
|
|
--chart-3: hsl(20 90% 65%);
|
|
--chart-4: hsl(150 55% 55%);
|
|
--chart-5: hsl(330 75% 70%);
|
|
|
|
--sidebar: hsl(265 11% 10%);
|
|
--sidebar-foreground: hsl(40 10% 88%);
|
|
--sidebar-primary: hsl(265 85% 70%);
|
|
--sidebar-primary-foreground: hsl(265 40% 12%);
|
|
--sidebar-accent: hsl(265 8% 18%);
|
|
--sidebar-accent-foreground: hsl(40 12% 94%);
|
|
--sidebar-border: hsl(265 8% 22%);
|
|
--sidebar-ring: hsl(265 85% 70%);
|
|
}
|
|
|
|
@layer base {
|
|
*,
|
|
::after,
|
|
::before,
|
|
::backdrop,
|
|
::file-selector-button {
|
|
border-color: var(--color-border, currentcolor);
|
|
}
|
|
|
|
html,
|
|
body,
|
|
#root {
|
|
height: 100%;
|
|
}
|
|
|
|
body {
|
|
@apply bg-background text-foreground;
|
|
/* The OS owns its own scroll regions; the page itself never scrolls. */
|
|
overflow: hidden;
|
|
overscroll-behavior: none;
|
|
font-family: "Inter Variable", -apple-system, BlinkMacSystemFont, "Segoe UI",
|
|
ui-sans-serif, system-ui, "Helvetica Neue", Arial, sans-serif;
|
|
font-feature-settings: "cv11", "ss01";
|
|
font-variation-settings: "opsz" 32;
|
|
-webkit-font-smoothing: antialiased;
|
|
text-rendering: optimizeLegibility;
|
|
}
|
|
|
|
:focus-visible {
|
|
outline: 2px solid var(--ring);
|
|
outline-offset: 2px;
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------- OS shell */
|
|
|
|
/*
|
|
* The desktop dot grid. A single repeating radial-gradient rather than an
|
|
* image, so it recolours with the theme and costs nothing to load.
|
|
*/
|
|
.os-desktop-surface {
|
|
background-color: var(--os-desktop);
|
|
background-image: radial-gradient(var(--os-desktop-dot) 1px, transparent 1px);
|
|
background-size: 22px 22px;
|
|
background-position: -1px -1px;
|
|
}
|
|
|
|
/* While a window is being dragged or resized, nothing else should react. */
|
|
body.os-dragging {
|
|
cursor: inherit;
|
|
user-select: none;
|
|
}
|
|
|
|
body.os-dragging iframe,
|
|
body.os-dragging .os-window-content {
|
|
pointer-events: none;
|
|
}
|
|
|
|
.os-window {
|
|
box-shadow: var(--os-shadow-idle);
|
|
transition: box-shadow 160ms ease;
|
|
}
|
|
|
|
.os-window[data-focused="true"] {
|
|
box-shadow: var(--os-shadow-focused);
|
|
}
|
|
|
|
.os-menubar-surface {
|
|
background-color: var(--os-menubar);
|
|
backdrop-filter: blur(14px) saturate(180%);
|
|
-webkit-backdrop-filter: blur(14px) saturate(180%);
|
|
}
|
|
|
|
/* Slim scrollbars so dense app content does not get a chunky OS bar. */
|
|
.os-scroll {
|
|
scrollbar-width: thin;
|
|
scrollbar-color: var(--border) transparent;
|
|
}
|
|
|
|
.os-scroll::-webkit-scrollbar {
|
|
width: 10px;
|
|
height: 10px;
|
|
}
|
|
|
|
.os-scroll::-webkit-scrollbar-track {
|
|
background: transparent;
|
|
}
|
|
|
|
.os-scroll::-webkit-scrollbar-thumb {
|
|
background-color: var(--border);
|
|
border-radius: 9999px;
|
|
border: 3px solid transparent;
|
|
background-clip: content-box;
|
|
}
|
|
|
|
.os-scroll::-webkit-scrollbar-thumb:hover {
|
|
background-color: var(--muted-foreground);
|
|
}
|
|
|
|
/* ------------------------------------------------------------- Animations */
|
|
|
|
/*
|
|
* Windows carry their position in an inline `transform: translate3d(...)`, so
|
|
* the opening animation must not touch `transform` — animating it would pin
|
|
* the window at 0,0 for the duration. The standalone `scale` property
|
|
* composes with the inline transform instead of replacing it.
|
|
*/
|
|
@keyframes os-window-in {
|
|
from {
|
|
opacity: 0;
|
|
scale: 0.96;
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
scale: 1;
|
|
}
|
|
}
|
|
|
|
@keyframes os-fade-in {
|
|
from {
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
.os-window {
|
|
animation: os-window-in 160ms cubic-bezier(0.22, 1, 0.36, 1);
|
|
}
|
|
|
|
.os-fade-in {
|
|
animation: os-fade-in 200ms ease-out;
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.os-window {
|
|
transition: none;
|
|
animation: os-fade-in 120ms ease-out;
|
|
}
|
|
}
|