Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

184 linesCodeBlameRaw
1@import "tailwindcss" source(".");
2@import "@g1t/theme/tokens.css";
3@import "@g1t/theme/fonts.css";
4
5/* Tailwind's names for the shared g1t tokens, as in apps/web. */
6@theme {
7 --font-sans: var(--g1t-font-sans);
8 --font-display: var(--g1t-font-display);
9 --font-mono: var(--g1t-font-mono);
10
11 /*
12 * Every colour is a pair: light-dark() picks the light palette or the
13 * dark one by the page's color-scheme, which follows the Appearance
14 * switch (lib/theme.ts), as on g1t.sh.
15 */
16 --color-bg: light-dark(var(--g1t-light-bg), var(--g1t-bg));
17 --color-surface: light-dark(var(--g1t-light-surface), var(--g1t-surface));
18 --color-raised: light-dark(var(--g1t-light-raised), var(--g1t-raised));
19 --color-line: light-dark(var(--g1t-light-line), var(--g1t-line));
20 --color-line-strong: light-dark(var(--g1t-light-line-strong), var(--g1t-line-strong));
21
22 --color-fg: light-dark(var(--g1t-light-fg), var(--g1t-fg));
23 --color-fg-soft: light-dark(var(--g1t-light-fg-soft), var(--g1t-fg-soft));
24 /* A primary button's hover: brighter than fg on dark, softer on light. */
25 --color-fg-hover: light-dark(#3a3a41, #ffffff);
26 --color-muted: light-dark(var(--g1t-light-muted), var(--g1t-muted));
27 --color-faint: light-dark(var(--g1t-light-faint), var(--g1t-faint));
28
29 --color-accent: light-dark(var(--g1t-light-accent), var(--g1t-accent));
30 --color-accent-hover: light-dark(var(--g1t-light-accent-hover), var(--g1t-accent-hover));
31 --color-accent-dim: light-dark(var(--g1t-light-accent-dim), var(--g1t-accent-dim));
32
33 --color-success: light-dark(var(--g1t-light-success), var(--g1t-success));
34 --color-success-dim: light-dark(var(--g1t-light-success-dim), var(--g1t-success-dim));
35
36 --color-info: light-dark(var(--g1t-light-info), var(--g1t-info));
37 --color-merged: light-dark(var(--g1t-light-merged), var(--g1t-merged));
38 --color-warn: light-dark(var(--g1t-light-warn), var(--g1t-warn));
39 --color-danger: light-dark(var(--g1t-light-danger), var(--g1t-danger));
40}
41
42/*
43 * Appearance (lib/theme.ts): with no choice, or Auto, the page follows
44 * the system; a choice of Light or Dark is data-theme on <html>, drawn by
45 * the server, so a page never shows the other theme first.
46 */
47html {
48 color-scheme: light dark;
49}
50html[data-theme="light"] {
51 color-scheme: light;
52}
53html[data-theme="dark"] {
54 color-scheme: dark;
55}
56
57body {
58 @apply bg-bg font-sans text-fg antialiased;
59}
60
61::selection {
62 background: color-mix(in srgb, var(--color-accent) 35%, transparent);
63}
64
65@layer base {
66 :focus-visible {
67 outline: 2px solid var(--color-accent);
68 outline-offset: 2px;
69 }
70
71 .grid > * {
72 min-width: 0;
73 }
74
75 /* Page titles in the headline face, as on g1t.sh. */
76 :where(h1) {
77 font-family: var(--font-display);
78 font-optical-sizing: auto;
79 }
80
81 :root {
82 accent-color: var(--color-accent);
83 }
84
85 /* Figures line up in columns of money. */
86 .tabular {
87 font-variant-numeric: tabular-nums;
88 }
89}
90
91/*
92 * A long-running button says it is running with CSS alone, before any
93 * script has loaded: a clicked button keeps focus while the old page waits
94 * for the response (components/progress.tsx lets go of it once a <Form>'s
95 * answer is in). `:focus-visible` is left out, so tabbing to it does not
96 * read as running.
97 */
98.run-button .run-busy,
99.run-button + .run-note {
100 display: none;
101}
102.run-button:focus:not(:focus-visible) .run-idle {
103 display: none;
104}
105.run-button:focus:not(:focus-visible) .run-busy {
106 display: inline-flex;
107 align-items: center;
108 gap: 0.5rem;
109}
110.run-button:focus:not(:focus-visible) + .run-note {
111 display: inline;
112}
113.run-spinner {
114 width: 0.8rem;
115 height: 0.8rem;
116 border-radius: 9999px;
117 border: 2px solid currentColor;
118 border-right-color: transparent;
119 animation: run-spin 0.8s linear infinite;
120}
121@keyframes run-spin {
122 to {
123 transform: rotate(360deg);
124 }
125}
126@media (prefers-reduced-motion: reduce) {
127 .run-spinner {
128 animation: none;
129 }
130}
131
132/*
133 * Every other submit button says it is working the same way: a clicked
134 * button keeps focus while the page waits for the answer, so it dims
135 * and shows a spinner. The page that comes back is
136 * fresh (every action redirects), and says what happened. Nothing may
137 * stop pointer events here: focus comes on mouse-down, before the click.
138 */
139form button[type="submit"]:not(.run-button):focus:not(:focus-visible) {
140 opacity: 0.75;
141}
142form button[type="submit"]:not(.run-button):focus:not(:focus-visible)::before {
143 content: "";
144 flex: none;
145 width: 0.8rem;
146 height: 0.8rem;
147 border-radius: 9999px;
148 border: 2px solid currentColor;
149 border-right-color: transparent;
150 animation: run-spin 0.8s linear infinite;
151}
152form button[type="submit"]:not(.run-button):focus:not(:focus-visible) > svg {
153 display: none;
154}
155@media (prefers-reduced-motion: reduce) {
156 form button[type="submit"]:focus:not(:focus-visible)::before {
157 animation: none;
158 }
159}
160
161/*
162 * The credit and terms forms (components/billing.tsx) show a field only for the
163 * choice it belongs to, with CSS alone: the custom amount and the slug for
164 * Custom, a refund's details for Refund, the expiry for anything else, and
165 * the date for "On a date". Where `:has()` is not supported (or without
166 * CSS) every field shows, and the server ignores the ones that do not apply.
167 */
168@supports selector(:has(*)) {
169 .credit-form .when-custom,
170 .credit-form .when-refund,
171 .credit-form .when-date,
172 .terms-form .when-custom {
173 display: none;
174 }
175 .credit-form:has(input[name="preset"][value="custom"]:checked) .when-custom,
176 .terms-form:has(input[name="preset"][value="custom"]:checked) .when-custom,
177 .credit-form:has(input[name="kind"][value="refund"]:checked) .when-refund,
178 .credit-form:has(input[name="expires"][value="date"]:checked) .when-date {
179 display: block;
180 }
181 .credit-form:has(input[name="kind"][value="refund"]:checked) .when-not-refund {
182 display: none;
183 }
184}