Files
goodgo-platform/apps/web/app/globals.css
Ho Ngoc Hai b3836d8d0f feat(notifications): wire publisher + async feature flag (GOO-173)
Phase 1 step 3 — make NotificationsPublisher available via DI and
introduce the NOTIFICATIONS_ASYNC_ENABLED flag that will gate the
listener cutover.

- NotificationsAsyncConfig: tiny injectable that reads
  NOTIFICATIONS_ASYNC_ENABLED (truthy: 1/true/yes/on, default disabled).
  Callers don't touch process.env directly; per-category rollout can be
  added later without churn in listeners.
- NotificationsModule providers/exports now include NotificationsPublisher
  and NotificationsAsyncConfig so listeners/handlers can inject them.
- Tests (14 specs on the flag + 4 on the publisher = 18 total green):
  * default disabled when unset
  * truthy parsing (1, true, TRUE, yes, on, padded)
  * falsy parsing (false, 0, no, off, empty, unknown)
  * describe() reports human-readable state

No call sites updated yet — next commit migrates the first listener
(PaymentCompletedListener pilot) onto the publisher with the flag check.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-24 14:26:10 +07:00

216 lines
5.2 KiB
CSS

@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
/* Light mode override (dark-first architecture) */
--background: 0 0% 97%;
--background-elevated: 0 0% 100%;
--background-surface: 220 14% 96%;
--foreground: 220 20% 12%;
--foreground-muted: 215 12% 45%;
--foreground-dim: 215 14% 45%;
--card: 0 0% 100%;
--card-foreground: 220 20% 12%;
--primary: 142.1 76.2% 36.3%;
--primary-foreground: 355.7 100% 97.3%;
--primary-hover: 142.1 76.2% 30%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--accent-blue: 210 100% 45%;
--accent-purple: 270 70% 50%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--success: 142.1 76.2% 36.3%;
--warning: 45 93% 47%;
--signal-up: 142 72% 38%;
--signal-up-bg: 142 72% 38%;
--signal-down: 0 84% 55%;
--signal-down-bg: 0 84% 55%;
--signal-neutral: 45 93% 45%;
--signal-neutral-bg: 45 93% 45%;
--border: 220 13% 88%;
--border-strong: 220 13% 78%;
--input: 214.3 31.8% 91.4%;
--ring: 142.1 76.2% 36.3%;
--radius: 0.5rem;
/* Chart palette (light) */
--chart-1: 200 90% 45%;
--chart-2: 142 65% 38%;
--chart-3: 38 95% 48%;
--chart-4: 280 65% 50%;
--chart-5: 0 75% 50%;
--chart-6: 180 60% 40%;
/* Motion */
--duration-xs: 80ms;
--duration-sm: 150ms;
--duration-md: 240ms;
--ease-standard: cubic-bezier(.2, 0, 0, 1);
--ease-emphasized: cubic-bezier(.3, 0, 0, 1);
}
.dark {
/* Terminal dark theme (primary) */
--background: 220 20% 4%;
--background-elevated: 220 18% 7%;
--background-surface: 220 16% 10%;
--foreground: 210 20% 90%;
--foreground-muted: 215 15% 55%;
--foreground-dim: 215 12% 70%;
--card: 220 18% 7%;
--card-foreground: 210 20% 90%;
--primary: 142 72% 42%;
--primary-foreground: 0 0% 100%;
--primary-hover: 142 72% 36%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 15% 55%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--accent-blue: 210 100% 56%;
--accent-purple: 270 70% 60%;
--destructive: 0 84% 60%;
--destructive-foreground: 210 40% 98%;
--success: 142 72% 42%;
--warning: 45 93% 58%;
--signal-up: 142 72% 50%;
--signal-up-bg: 142 72% 50%;
--signal-down: 0 84% 60%;
--signal-down-bg: 0 84% 60%;
--signal-neutral: 45 93% 58%;
--signal-neutral-bg: 45 93% 58%;
--border: 218 16% 16%;
--border-strong: 218 16% 24%;
--input: 217.2 32.6% 17.5%;
--ring: 142 72% 42%;
}
.dark {
/* Chart palette (dark) */
--chart-1: 200 90% 60%;
--chart-2: 142 70% 50%;
--chart-3: 38 95% 60%;
--chart-4: 280 70% 65%;
--chart-5: 0 75% 60%;
--chart-6: 180 65% 50%;
}
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
}
/* Data/number cells: tabular-nums for alignment */
.font-mono,
[data-numeric] {
font-variant-numeric: tabular-nums;
}
/* Consistent focus-visible ring for all interactive elements */
:focus-visible {
outline: 2px solid hsl(var(--ring));
outline-offset: 2px;
border-radius: var(--radius);
}
/* Remove outline for mouse users; keep it for keyboard navigation */
:focus:not(:focus-visible) {
outline: none;
}
}
/* Mapbox popup theming */
.mapboxgl-popup-content {
background: hsl(var(--card));
color: hsl(var(--card-foreground));
border: 1px solid hsl(var(--border));
}
.mapboxgl-popup-tip {
border-top-color: hsl(var(--card)) !important;
border-bottom-color: hsl(var(--card)) !important;
}
.mapboxgl-ctrl button {
background-color: hsl(var(--card));
color: hsl(var(--card-foreground));
}
.mapboxgl-ctrl button:hover {
background-color: hsl(var(--accent));
}
.mapboxgl-ctrl-attrib {
background: hsl(var(--card) / 0.8);
color: hsl(var(--muted-foreground));
}
.mapboxgl-ctrl-attrib a {
color: hsl(var(--muted-foreground));
}
/* Ticker scroll animation */
@keyframes ticker-scroll {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
.animate-ticker {
animation: ticker-scroll 60s linear infinite;
}
.animate-ticker:hover {
animation-play-state: paused;
}
/* Signal flash for price updates (tick-flash per design tokens) */
@keyframes tick-up {
0% {
background-color: hsl(var(--signal-up) / 0.18);
}
100% {
background-color: transparent;
}
}
@keyframes tick-down {
0% {
background-color: hsl(var(--signal-down) / 0.18);
}
100% {
background-color: transparent;
}
}
.tick-flash-up {
animation: tick-up 480ms ease-out;
}
.tick-flash-down {
animation: tick-down 480ms ease-out;
}
/* Legacy aliases */
.flash-up {
animation: tick-up 480ms ease-out;
}
.flash-down {
animation: tick-down 480ms ease-out;
}
@media (prefers-reduced-motion: reduce) {
.tick-flash-up,
.tick-flash-down,
.flash-up,
.flash-down {
animation: none;
}
.animate-ticker {
animation: none;
}
}