Files
pig/apps/web/src/index.css
T
karti 9db53cb36f
CI / verify (push) Successful in 3m26s
CI / publish (push) Has been skipped
Polish the public auth experience
Replace the stacked sign-in card with a responsive public shell, a local monochrome compute field, restrained grain and reduced-motion-safe drift. Share the anonymous header with Learn so public navigation stays consistent, and carry the corrected spacing through registration and profile setup.
2026-08-13 20:51:34 -07:00

263 lines
7.5 KiB
CSS

@tailwind base;
@tailwind components;
@tailwind utilities;
@font-face {
font-family: 'Manrope Variable';
font-style: normal;
font-display: swap;
font-weight: 200 800;
src: url('@fontsource-variable/manrope/files/manrope-latin-wght-normal.woff2') format('woff2-variations');
}
/*
* Surfaces and neutrals.
*
* Accent variables are NOT defined here — they are written onto the root
* element at runtime from the palette in @pig/core, so there is exactly one
* definition of each colour and the CSS cannot drift from the TypeScript.
*/
:root {
--bg: 0 0% 100%;
--surface: 0 0% 100%;
--surface-2: 240 5% 97%;
--border: 240 6% 90%;
--fg: 240 10% 4%;
--muted: 240 4% 46%;
--positive: 160 84% 32%;
--warning: 32 95% 44%;
--danger: 0 72% 45%;
--info: 201 90% 40%;
--shadow: 240 10% 4%;
/* Safe-area insets, so layout can reference them even at zero. */
--safe-top: env(safe-area-inset-top, 0px);
--safe-bottom: env(safe-area-inset-bottom, 0px);
--safe-left: env(safe-area-inset-left, 0px);
--safe-right: env(safe-area-inset-right, 0px);
/*
* The application header's height, including the notch it sits under. Both
* side panes are sticky beneath it and subtract this from the viewport, so
* it has to be one number rather than a magic constant in three files.
*/
--app-header-h: calc(3.5rem + var(--safe-top));
/*
* The sidebar primitive's palette.
*
* Aliases, not values. shadcn's sidebar block wants its own `--sidebar-*`
* scale; defining literal colours here would be a second palette to keep in
* step with the first, and it would not follow the user's accent — which is
* written onto this same element at runtime by lib/theme.tsx. Because these
* are HSL channel triples pointing at other HSL channel triples, the dark
* theme and every accent flow through for free and there is nothing to
* duplicate under [data-theme='dark'].
*/
--sidebar-background: var(--surface);
--sidebar-foreground: var(--fg);
--sidebar-primary: var(--accent);
--sidebar-primary-foreground: var(--accent-on);
--sidebar-accent: var(--accent-subtle);
--sidebar-accent-foreground: var(--accent-fg);
--sidebar-border: var(--border);
--sidebar-ring: var(--accent);
}
@media (min-width: 1024px) {
:root {
--app-header-h: calc(4rem + var(--safe-top));
}
}
:root[data-theme='dark'] {
--bg: 240 10% 4%;
--surface: 240 8% 7%;
--surface-2: 240 6% 11%;
--border: 240 5% 18%;
--fg: 0 0% 98%;
--muted: 240 5% 60%;
--positive: 158 64% 52%;
--warning: 38 92% 60%;
--danger: 0 84% 65%;
--info: 199 89% 60%;
--shadow: 0 0% 0%;
}
@layer base {
* {
border-color: hsl(var(--border));
}
html {
/* Prevents iOS from silently enlarging text in landscape. */
-webkit-text-size-adjust: 100%;
/* Keeps the background painted behind the rubber-band overscroll area. */
background-color: hsl(var(--bg));
}
body {
@apply bg-bg text-fg antialiased;
font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
/* Stops the whole page bouncing on iOS while inner panes still scroll. */
overscroll-behavior-y: none;
}
::selection {
background: hsl(var(--accent-subtle));
color: hsl(var(--accent-fg));
}
/*
* Mobile Safari zooms the viewport when a focused input has a font size
* below 16px, and never zooms back out. This is the fix — not
* user-scalable=no, which breaks pinch-zoom for everyone.
*/
input,
select,
textarea {
font-size: max(16px, 1rem);
}
/* A visible, consistent focus ring — keyboard users need it, and the
default varies wildly between browsers. */
:focus-visible {
@apply outline-none ring-2 ring-accent ring-offset-2 ring-offset-bg;
}
/* Respect a reduced-motion preference rather than animating regardless. */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
}
@layer components {
/* Horizontal scrollers (pipeline boards, wide tables) get momentum
scrolling and snap on touch, without hijacking the page. */
.scroll-x {
@apply overflow-x-auto;
-webkit-overflow-scrolling: touch;
scrollbar-width: thin;
overscroll-behavior-x: contain;
}
.scroll-x::-webkit-scrollbar {
height: 6px;
}
.scroll-x::-webkit-scrollbar-thumb {
background-color: hsl(var(--border));
border-radius: 3px;
}
/* Touch targets. 44px is the documented iOS minimum and the difference
between a usable and a frustrating phone experience. */
.tap {
@apply min-h-[44px] min-w-[44px];
}
.card {
@apply rounded-2xl border border-border/90 bg-surface;
box-shadow: 0 1px 2px hsl(var(--shadow) / 0.035), 0 12px 36px hsl(var(--shadow) / 0.025);
}
.app-canvas {
background-color: hsl(var(--bg));
background-image:
radial-gradient(circle at 78% -12%, hsl(var(--accent-subtle) / 0.7), transparent 34rem),
linear-gradient(to bottom, hsl(var(--surface) / 0.25), transparent 28rem);
background-attachment: fixed;
}
/*
* The auth surface is intentionally monochrome even if a signed-in user has
* selected another accent. It is public brand chrome, and forcing the local
* dark token set keeps controls legible without changing the stored theme.
*/
.auth-shell {
--bg: 240 10% 3%;
--surface: 240 7% 6%;
--surface-2: 240 5% 11%;
--border: 240 4% 22%;
--fg: 0 0% 98%;
--muted: 240 5% 67%;
--accent: 0 0% 98%;
--accent-fg: 0 0% 98%;
--accent-on: 240 6% 10%;
--accent-subtle: 240 4% 16%;
color-scheme: dark;
}
.auth-field-motion {
animation: auth-field-drift 26s ease-in-out infinite alternate;
will-change: transform;
}
.auth-visual {
opacity: 0.52;
}
.auth-visual::after {
position: absolute;
inset: 0;
content: '';
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 180 180' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.92' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.72'/%3E%3C/svg%3E");
background-size: 180px 180px;
mix-blend-mode: soft-light;
opacity: 0.14;
}
.auth-visual-vignette {
background:
linear-gradient(to bottom, hsl(var(--bg) / 0.18), hsl(var(--bg) / 0.72) 34%, hsl(var(--bg)) 100%),
linear-gradient(to right, hsl(var(--bg) / 0.18), hsl(var(--bg) / 0.42));
}
@media (min-width: 1024px) {
.auth-visual {
opacity: 1;
}
.auth-visual-vignette {
background:
linear-gradient(to right, transparent 48%, hsl(var(--bg) / 0.28) 78%, hsl(var(--bg) / 0.72)),
linear-gradient(to bottom, hsl(var(--bg) / 0.06), transparent 18%, hsl(var(--bg) / 0.28));
}
}
@keyframes auth-field-drift {
0% {
transform: translate3d(-1.4%, -0.8%, 0) scale(1.045);
}
50% {
transform: translate3d(0.8%, 0.6%, 0) scale(1.075);
}
100% {
transform: translate3d(-0.3%, 1.1%, 0) scale(1.055);
}
}
@media (prefers-reduced-motion: reduce) {
.auth-field-motion {
animation: none;
transform: scale(1.045);
will-change: auto;
}
}
/* Tabular figures keep money and percentages from jittering as they
update, which matters on a dashboard that refreshes. */
.nums {
font-variant-numeric: tabular-nums;
font-feature-settings: 'tnum';
}
}