fuzzyworld-tools / commit
Add shared design system and icon system
server/site/_icons.css | 212 ++++++++++++++ server/site/_shared.css | 458 ++++++++++++++++++++++++++++++ server/site/main.css | 269 ++++++++++++++++++ server/site/style.css | 730 ++++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 1579 insertions(+), 90 deletions(-) diff --git a/server/site/_icons.css b/server/site/_icons.css new file mode 100644 index 0000000..f5b3139 --- /dev/null +++ b/server/site/_icons.css @@ -0,0 +1,212 @@ +/* ============================================================================= + FuzzyWorld Icon System + + Icon classes using inline SVG data URIs. + All icons are self-contained and work without external dependencies. + + Usage: + <span class="icon icon-vscode"></span> + <a href="..." class="icon-link icon-github">GitHub</a> + + For links that go to external sites: + <a href="https://..." class="external">Link text</a> + This will automatically add the external icon via ::after + ============================================================================= */ + +/* --------------------------------------------------------------------- + Icon Base Styles + --------------------------------------------------------------------- */ + +.icon { + display: inline-block; + width: 1em; + height: 1em; + vertical-align: -0.125em; + fill: currentColor; + flex-shrink: 0; + background-size: contain; + background-repeat: no-repeat; + background-position: center; +} + +/* Icon with text - adds proper spacing */ +.icon-text { + display: inline-flex; + align-items: center; + gap: 0.25em; +} + +.icon-text .icon { + width: 0.9em; + height: 0.9em; +} + +/* --------------------------------------------------------------------- + External Link Indicator + Automatically added to external links (not fuzzyworld.net) + --------------------------------------------------------------------- */ + +a[href^="http"]:not([href*="fuzzyworld.net"]):not(.no-external)::after, +a[href^="mailto:"]::after, +a[href^="vscode://"]::after { + content: " "; + display: inline-block; + width: 0.8em; + height: 0.8em; + vertical-align: 0.05em; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3Cpolyline points='15 3 21 3 21 9'/%3E%3Cline x1='10' y1='14' x2='21' y2='3'/%3E%3C/svg%3E"); + opacity: 0.6; +} + +/* Disable external icon for specific links */ +.no-external::after { + content: none; +} + +/* --------------------------------------------------------------------- + Brand & Platform Icons + --------------------------------------------------------------------- */ + +/* FuzzyWorld logo placeholder */ +.icon-fuzzyworld::before { + content: "f"; + font-weight: 700; + font-size: 1.2em; + letter-spacing: -0.02em; +} + +/* VS Code Icon - Simple V in a diamond */ +.icon-vscode { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='currentColor' d='M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5'/%3E%3C/svg%3E"); +} + +/* GitHub Octocat Icon */ +.icon-github { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.163.798-.254.798-.554 0-.297-.006-.597-.006-.898 0-.297.006-.597.006-.898.598-.162.797-.66 7.983-1.178.708-.518 1.116-1.357 1.116-2.272 0-.298-.006-.598-.006-.898 0-.298.006-.598.006-.898.598-.163.797-.662 7.983-1.179.708-.518 1.116-1.356 1.116-2.272 0-.897-.006-1.796-.006-2.694 0-.298-.006-.598-.006-.898 0-.298.006-.598.006-.898.598-.163.797-.662 7.983-1.179.708-.518 1.116-1.357 1.116-2.272 0-.897-.006-1.796-.006-2.694 0-.297-.006-.597-.006-.897 0-.297.006-.597.006-.897.598-.162.797-.66 7.983-1.178.708-.518 1.116-1.357 1.116-2.272 0-.898-.006-1.797-.006-2.695C13.438 1.6 14 0 12 0zm0 2.157c6.626 0 12 5.373 12 12s-5.374 12-12 12S0 17.373 0 12 5.374 0 12 0zm-3.35 15.257l-2.077-2.077-1.116 1.116 2.077 2.077 1.116-1.116zm1.116-2.592l-1.116-1.116-1.116 1.116 1.116 1.116 1.116-1.116z'/%3E%3C/svg%3E"); +} + +/* Git Icon */ +.icon-git { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.163.798-.254.798-.554 0-.297-.006-.597-.006-.898 0-.297.006-.597.006-.898.598-.162.797-.66 7.983-1.178.708-.518 1.116-1.357 1.116-2.272 0-.298-.006-.598-.006-.898 0-.298.006-.598.006-.898.598-.163.797-.662 7.983-1.179.708-.518 1.116-1.356 1.116-2.272 0-.897-.006-1.796-.006-2.694 0-.298-.006-.598-.006-.898 0-.298.006-.598.006-.898.598-.163.797-.662 7.983-1.179.708-.518 1.116-1.357 1.116-2.272C6.811 2.57 5.373 1.5 12 1.5c6.626 0 12 5.373 12 12s-5.374 12-12 12z'/%3E%3C/svg%3E"); +} + +/* Code Icon */ +.icon-code { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='m15.5 10.5-2-2-2 2-2-2-2 2-2-2-2 2 2 2-2-2 2 2 2-2 2-2 2-2-2 2 2z'/%3E%3C/svg%3E"); +} + +/* --------------------------------------------------------------------- + File & Directory Icons + --------------------------------------------------------------------- */ + +/* Folder Icon */ +.icon-folder { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2-3h9a2 2 0 0 1 2 2z'/%3E%3C/svg%3E"); +} + +/* File Icon */ +.icon-file { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z'/%3E%3Cpolyline points='14 2 14 8 20 8'/%3E%3Cline x1='16' y1='13' x2='8' y2='13'/%3E%3Cline x1='16' y1='17' x2='8' y2='17'/%3E%3Cpath d='M10 20h4'/%3E%3C/svg%3E"); +} + +/* File Code Icon */ +.icon-file-code { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V8z'/%3E%3Cpolyline points='14 2 14 8 20 8'/%3E%3Cline x1='16' y1='13' x2='8' y2='13'/%3E%3Cline x1='16' y1='17' x2='8' y2='17'/%3E%3Cpolyline points='10 9 9 9 8 9'/%3E%3Cpolyline points='17 9 18 9 19 9'/%3E%3C/svg%3E"); +} + +/* Image Icon */ +.icon-image { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h18a2 2 0 0 1 2 2v14zM10 15l5-5-5-5v10z'/%3E%3C/svg%3E"); +} + +/* Markdown Icon */ +.icon-markdown { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z'/%3E%3Cpolyline points='14 2 14 8 20 8'/%3E%3Cline x1='16' y1='13' x2='8' y2='13'/%3E%3Cline x1='16' y1='17' x2='8' y2='17'/%3E%3Cpolyline points='10 9 9 9 8 9'/%3E%3Cpolyline points='17 9 18 9 19 9'/%3E%3C/svg%3E"); +} + +/* --------------------------------------------------------------------- + UI & Status Icons + --------------------------------------------------------------------- */ + +/* Checkmark / Success Icon */ +.icon-check { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E"); +} + +/* X / Error Icon */ +.icon-x { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'/%3E%3Cline x1='6' y1='6' x2='18' y2='18'/%3E%3C/svg%3E"); +} + +/* Circle / Running Icon (spinner-like) */ +.icon-circle { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3C/svg%3E"); +} + +/* Alert / Warning Icon */ +.icon-alert { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cline x1='12' y1='8' x2='12' y2='12'/%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'/%3E%3C/svg%3E"); +} + +/* Clock / Time Icon */ +.icon-clock { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpolyline points='12 6 12 12 16 14'/%3E%3C/svg%3E"); +} + +/* Arrow Right (for navigation) */ +.icon-arrow-right { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'/%3E%3Cpolyline points='12 5 19 12 12 19'/%3E%3C/svg%3E"); +} + +/* Arrow Up Right (external link style) */ +.icon-arrow-up-right { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='7' y1='17' x2='17' y2='7'/%3E%3Cpolyline points='7 7 17 7 17 17'/%3E%3C/svg%3E"); +} + +/* --------------------------------------------------------------------- + Application Icons + --------------------------------------------------------------------- */ + +/* App Icon (generic) */ +.icon-app { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z'/%3E%3Cpath d='M12 6l4 4-4 4-4-4 4-4z'/%3E%3C/svg%3E"); +} + +/* Web Icon */ +.icon-web { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cline x1='2' y1='12' x2='22' y2='12'/%3E%3Cpath d='M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z'/%3E%3C/svg%3E"); +} + +/* --------------------------------------------------------------------- + Helper Classes for Icon + Text Combinations + --------------------------------------------------------------------- */ + +/* VS Code link with icon */ +.vscode-link { + display: inline-flex; + align-items: center; + gap: 0.35em; +} + +.vscode-link::before { + content: ""; + display: inline-block; + width: 1em; + height: 1em; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='currentColor' d='M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5'/%3E%3C/svg%3E"); + background-size: contain; + background-repeat: no-repeat; + background-position: center; +} + +/* Clone command icon */ +.clone-icon::before { + content: ""; + display: inline-block; + width: 1em; + height: 1em; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M16 22s2-4-2-4-2 4-2 4-4-2-2-4-2 4-2 4-4-2-2 4-6-4 2-2 4 2 2 6-4-2-2 4 6-2 4 2 2 4 6 4'/%3E%3C/svg%3E"); + background-size: contain; + background-repeat: no-repeat; + background-position: center; +} diff --git a/server/site/_shared.css b/server/site/_shared.css new file mode 100644 index 0000000..17b7c23 --- /dev/null +++ b/server/site/_shared.css @@ -0,0 +1,458 @@ +/* ============================================================================= + FuzzyWorld Shared Design System + + This file contains design tokens and common base styles shared across + all fuzzyworld.net sites (fuzzyworld.net, code.fuzzyworld.net, apps.fuzzyworld.net). + + Import this file before site-specific styles. + ============================================================================= */ + +/* --------------------------------------------------------------------- + Design Tokens: Colors + --------------------------------------------------------------------- */ + +:root { + /* Light mode (default) */ + --color-bg-primary: #ffffff; + --color-bg-secondary: #f6f6f8; + --color-bg-tertiary: #ffffff; + --color-bg-hover: #eaeaf0; + + --color-fg-primary: #1d1d1f; + --color-fg-secondary: #62626a; + --color-fg-muted: #9a9aa2; + + --color-border-primary: #e1e1e6; + --color-border-secondary: #c1c1c7; + + /* Accent colors */ + --color-accent: #0a58ca; + --color-accent-soft: #e8f0fe; + --color-accent-muted: #79b8ff; + + /* Semantic colors */ + --color-success: #116329; + --color-success-bg: #e6f4ea; + --color-error: #a40e26; + --color-error-bg: #fdecea; + --color-warning: #8250df; + --color-warning-bg: #e8e8ea; + + /* Code syntax colors (light) */ + --color-hl-comment: #6a737d; + --color-hl-keyword: #cf222e; + --color-hl-string: #0a3069; + --color-hl-number: #0550ae; + --color-hl-function: #8250df; + --color-hl-class: #953800; + --color-hl-type: #116329; +} + +/* Dark mode */ +@media (prefers-color-scheme: dark) { + :root { + --color-bg-primary: #111113; + --color-bg-secondary: #1a1a1e; + --color-bg-tertiary: #17171a; + --color-bg-hover: #2a2a2f; + + --color-fg-primary: #e8e8ea; + --color-fg-secondary: #9a9aa2; + --color-fg-muted: #62626a; + + --color-border-primary: #2a2a2f; + --color-border-secondary: #3e3e42; + + /* Accent colors */ + --color-accent: #79b8ff; + --color-accent-soft: #16243a; + --color-accent-muted: #bf8700; + + /* Semantic colors */ + --color-success: #7ee787; + --color-success-bg: #12261a; + --color-error: #ff9492; + --color-error-bg: #2d1517; + --color-warning: #c5a5ff; + --color-warning-bg: #2a2a2f; + + /* Code syntax colors (dark) */ + --color-hl-comment: #8b949e; + --color-hl-keyword: #ff7b72; + --color-hl-string: #a5d6ff; + --color-hl-number: #79c0ff; + --color-hl-function: #d2a8ff; + --color-hl-class: #ffa657; + --color-hl-type: #7ee787; + } +} + +/* Force color scheme support */ +:root { + color-scheme: light dark; +} + +/* --------------------------------------------------------------------- + Design Tokens: Spacing + --------------------------------------------------------------------- */ + +:root { + --spacing-xs: 0.25rem; + --spacing-sm: 0.5rem; + --spacing-md: 1rem; + --spacing-lg: 1.5rem; + --spacing-xl: 2rem; + --spacing-2xl: 3rem; + + /* For grid gaps */ + --gap-sm: 0.75rem; + --gap-md: 1rem; + --gap-lg: 1.5rem; +} + +/* --------------------------------------------------------------------- + Design Tokens: Typography + --------------------------------------------------------------------- */ + +:root { + --font-family-sans: system-ui, -apple-system, "Segoe UI", sans-serif; + --font-family-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + + --font-size-xs: 0.8rem; + --font-size-sm: 0.9rem; + --font-size-base: 1rem; + --font-size-lg: 1.1rem; + --font-size-xl: 1.2rem; + --font-size-2xl: 1.75rem; + + --font-weight-normal: 400; + --font-weight-medium: 500; + --font-weight-semibold: 600; + --font-weight-bold: 700; + + --line-height-tight: 1.25; + --line-height-normal: 1.5; + --line-height-relaxed: 1.55; +} + +/* --------------------------------------------------------------------- + Design Tokens: Borders & Shadows + --------------------------------------------------------------------- */ + +:root { + --border-radius-sm: 4px; + --border-radius-md: 6px; + --border-radius-lg: 8px; + --border-radius-full: 9999px; + + --box-shadow-sm: 0 1px 2px rgb(0 0 0 / 6%); + --box-shadow-md: 0 4px 6px rgb(0 0 0 / 8%); + + /* No shadow in dark mode */ + --box-shadow: var(--box-shadow-sm); +} + +@media (prefers-color-scheme: dark) { + :root { + --box-shadow: none; + } +} + +/* --------------------------------------------------------------------- + Design Tokens: Transitions + --------------------------------------------------------------------- */ + +:root { + --transition-fast: 100ms ease; + --transition-normal: 200ms ease; + --transition-slow: 300ms ease; +} + +/* --------------------------------------------------------------------- + Base Styles + --------------------------------------------------------------------- */ + +* { + box-sizing: border-box; +} + +html { + -webkit-text-size-adjust: 100%; + font-size: 16px; +} + +body { + margin: 0; + background: var(--color-bg-primary); + color: var(--color-fg-primary); + font: var(--font-size-base)/var(--line-height-relaxed) var(--font-family-sans); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* Text selection */ +::selection { + background: var(--color-accent); + color: var(--color-bg-primary); +} + +/* Focus styles */ +:focus-visible { + outline: 2px solid var(--color-accent); + outline-offset: 2px; + border-radius: var(--border-radius-sm); +} + +/* Links */ +a { + color: var(--color-accent); + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +/* Images */ +img, picture, video, canvas, svg { + display: block; + max-width: 100%; +} + +/* Lists */ +ul, ol { + padding: 0; + margin: 0; +} + +/* Forms */ +input, button, textarea, select { + font: inherit; +} + +/* Code */ +code, kbd, samp, pre { + font-family: var(--font-family-mono); +} + +/* Headings */ +h1, h2, h3, h4, h5, h6 { + font-weight: var(--font-weight-semibold); + line-height: var(--line-height-tight); + letter-spacing: -0.01em; +} + +/* --------------------------------------------------------------------- + Layout Utilities + --------------------------------------------------------------------- */ + +.main-container { + max-width: 72rem; + margin: 0 auto; + padding: 0 var(--spacing-md) var(--spacing-2xl); +} + +/* Visually hidden but accessible */ +.visually-hidden { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +/* Text utilities */ +.text-muted { color: var(--color-fg-muted); } +.text-secondary { color: var(--color-fg-secondary); } +.text-success { color: var(--color-success); } +.text-error { color: var(--color-error); } +.text-warning { color: var(--color-warning); } + +/* Truncate text */ +.truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* Overflow wrap */ +.overflow-wrap-anywhere { + overflow-wrap: anywhere; +} + +/* --------------------------------------------------------------------- + Spacing Utilities + --------------------------------------------------------------------- */ + +.mt-xs { margin-top: var(--spacing-xs); } +.mt-sm { margin-top: var(--spacing-sm); } +.mt-md { margin-top: var(--spacing-md); } +.mt-lg { margin-top: var(--spacing-lg); } +.mt-xl { margin-top: var(--spacing-xl); } + +.mb-xs { margin-bottom: var(--spacing-xs); } +.mb-sm { margin-bottom: var(--spacing-sm); } +.mb-md { margin-bottom: var(--spacing-md); } +.mb-lg { margin-bottom: var(--spacing-lg); } +.mb-xl { margin-bottom: var(--spacing-xl); } + +/* --------------------------------------------------------------------- + Card Component + --------------------------------------------------------------------- */ + +.card { + background: var(--color-bg-tertiary); + border: 1px solid var(--color-border-primary); + border-radius: var(--border-radius-lg); + box-shadow: var(--box-shadow); + padding: var(--spacing-md); +} + +.card:hover { + border-color: var(--color-accent); +} + +/* --------------------------------------------------------------------- + Button Component + --------------------------------------------------------------------- */ + +.btn { + display: inline-flex; + align-items: center; + gap: var(--spacing-xs); + padding: var(--spacing-xs) var(--spacing-md); + background: var(--color-accent); + color: var(--color-bg-primary); + border: none; + border-radius: var(--border-radius-md); + font: inherit; + font-size: var(--font-size-sm); + cursor: pointer; + transition: background var(--transition-fast); +} + +.btn:hover { + background: var(--color-accent-muted); +} + +.btn-secondary { + background: var(--color-bg-secondary); + color: var(--color-fg-primary); + border: 1px solid var(--color-border-primary); +} + +.btn-secondary:hover { + background: var(--color-bg-hover); +} + +/* --------------------------------------------------------------------- + Badge Component + --------------------------------------------------------------------- */ + +.badge { + display: inline-block; + padding: var(--spacing-xs) var(--spacing-sm); + background: var(--color-bg-secondary); + color: var(--color-fg-secondary); + border: 1px solid var(--color-border-primary); + border-radius: var(--border-radius-full); + font-size: var(--font-size-xs); +} + +.badge-success { + background: var(--color-success-bg); + color: var(--color-success); + border-color: var(--color-success); +} + +.badge-error { + background: var(--color-error-bg); + color: var(--color-error); + border-color: var(--color-error); +} + +.badge-warning { + background: var(--color-warning-bg); + color: var(--color-warning); + border-color: var(--color-warning); +} + +/* --------------------------------------------------------------------- + Table Styles + --------------------------------------------------------------------- */ + +table { + border-collapse: collapse; + width: 100%; +} + +td, th { + padding: var(--spacing-xs) var(--spacing-sm); + border-bottom: 1px solid var(--color-border-primary); + text-align: left; + vertical-align: top; +} + +th { + color: var(--color-fg-muted); + font-weight: var(--font-weight-medium); + font-size: var(--font-size-sm); +} + +table:not(.source) tr:hover > td { + background: var(--color-bg-secondary); +} + +/* --------------------------------------------------------------------- + Back to Top Button + --------------------------------------------------------------------- */ + +.back-to-top { + position: fixed; + bottom: var(--spacing-xl); + right: var(--spacing-xl); + width: 2.5rem; + height: 2.5rem; + display: flex; + align-items: center; + justify-content: center; + background: var(--color-bg-secondary); + border: 1px solid var(--color-border-primary); + border-radius: var(--border-radius-full); + color: var(--color-fg-primary); + font-size: 1.25rem; + cursor: pointer; + transition: all var(--transition-fast); + opacity: 0; + visibility: hidden; + z-index: 1000; +} + +.back-to-top:hover { + background: var(--color-bg-hover); +} + +.back-to-top.visible { + opacity: 1; + visibility: visible; +} + +/* --------------------------------------------------------------------- + Responsive Utilities + --------------------------------------------------------------------- */ + +@media (max-width: 64rem) { + .main-container { + padding: 0 var(--spacing-sm) var(--spacing-xl); + } +} + +@media (max-width: 40rem) { + .hide-on-mobile { + display: none !important; + } +} diff --git a/server/site/main.css b/server/site/main.css new file mode 100644 index 0000000..4931b11 --- /dev/null +++ b/server/site/main.css @@ -0,0 +1,269 @@ +/* ============================================================================= + FuzzyWorld Main Site Styles + + This file contains styles for fuzzyworld.net (the main site). + It builds upon the shared design system in _shared.css. + + Import order: + 1. _shared.css (design tokens and base styles) + 2. _icons.css (icon system) + 3. main.css (this file - main site specific styles) + ============================================================================= */ + +/* Import shared design system */ +@import url("_shared.css"); +@import url("_icons.css"); + +/* ============================================================================= + Main Site Configuration + ============================================================================= */ + +body { + max-width: 72rem; + margin: 0 auto; + padding: 0 var(--spacing-md); + background: var(--color-bg-primary); + color: var(--color-fg-primary); + font: 16px/var(--line-height-relaxed) var(--font-family-sans); +} + +/* ============================================================================= + Site Header + ============================================================================= */ + +h1 { + display: flex; + align-items: center; + gap: var(--spacing-sm); + font-size: var(--font-size-2xl); + font-weight: var(--font-weight-bold); + margin: var(--spacing-lg) 0; + padding: 0; +} + +#logo { + width: 3rem; + height: 3rem; + border-radius: var(--border-radius-md); +} + +/* ============================================================================= + Main Content + ============================================================================= */ + +> div { + padding: var(--spacing-sm) 0; +} + +> div > div { + padding: var(--spacing-md) 0; +} + +h2 { + font-size: var(--font-size-xl); + font-weight: var(--font-weight-semibold); + margin: var(--spacing-md) 0 var(--spacing-sm); +} + +h3 { + font-size: var(--font-size-lg); + font-weight: var(--font-weight-semibold); + margin: var(--spacing-md) 0 var(--spacing-sm); +} + +p { + line-height: var(--line-height-relaxed); + max-width: 46rem; +} + +/* ============================================================================= + Lists + ============================================================================= */ + +.list-horizontal { + list-style: none; + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: start; + justify-content: center; + margin: var(--spacing-md) 0; + padding: var(--spacing-md) 0; + white-space: nowrap; +} + +.list-horizontal li:not(:last-child)::after { + content: "|"; + margin: 0 var(--spacing-xs); + color: var(--color-fg-muted); +} + +.list-vertical { + list-style: none; + padding: 0; + margin: var(--spacing-sm) 0; +} + +.list-vertical li { + margin: var(--spacing-sm) 0; + display: flex; + align-items: center; + gap: var(--spacing-sm); +} + +/* ============================================================================= + Images & Logos + ============================================================================= */ + +img { + max-width: 100%; + height: auto; +} + +.small-logo { + width: 1em; + height: 1em; + flex-shrink: 0; +} + +/* ============================================================================= + Site Footer + ============================================================================= */ + +#footer { + font-size: var(--font-size-xs); + margin-top: var(--spacing-xl); + padding-top: var(--spacing-lg); + border-top: 1px solid var(--color-border-primary); + color: var(--color-fg-muted); +} + +#footer a { + color: var(--color-accent); + text-decoration: none; +} + +#footer a:hover { + text-decoration: underline; +} + +/* ============================================================================= + Ko-fi Widget + ============================================================================= */ + +#footer .kofi-widget { + display: block; + text-align: center; + margin: var(--spacing-md) 0; +} + +/* ============================================================================= + Shared Header & Footer Integration + ============================================================================= */ + +/* Import the shared site header and footer styles */ +header.site { + background: var(--color-bg-secondary); + border-bottom: 1px solid var(--color-border-primary); + margin-bottom: var(--spacing-lg); +} + +header.site .bar { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0 var(--spacing-lg); + padding: var(--spacing-sm) 0; +} + +header.site .brand { + font-weight: var(--font-weight-bold); + font-size: var(--font-size-lg); + color: var(--color-fg-primary); + letter-spacing: -0.01em; +} + +header.site .brand:hover { + text-decoration: none; + color: var(--color-accent); +} + +footer.site { + margin-top: var(--spacing-xl); + border-top: 1px solid var(--color-border-primary); + background: var(--color-bg-secondary); + color: var(--color-fg-muted); + font-size: var(--font-size-sm); +} + +footer.site .cols { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); + gap: var(--spacing-lg); + padding: var(--spacing-lg) 0 var(--spacing-xl); +} + +footer.site h2 { + font-size: var(--font-size-xs); + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--color-fg-muted); + margin: 0 0 var(--spacing-xs); +} + +footer.site a, +footer.site span { + display: block; + padding: var(--spacing-xs) 0; +} + +/* ============================================================================= + Site Navigation + ============================================================================= */ + +.site-nav { + display: flex; + flex-wrap: wrap; + gap: 0 var(--spacing-md); +} + +.site-nav a { + color: var(--color-fg-muted); + padding: var(--spacing-sm) 0; +} + +.site-nav a:hover { + color: var(--color-fg-primary); + text-decoration: none; +} + +.site-nav a[aria-current] { + color: var(--color-fg-primary); + font-weight: var(--font-weight-semibold); +} + +/* ============================================================================= + Responsive Adjustments + ============================================================================= */ + +@media (max-width: 64rem) { + h1 { + font-size: var(--font-size-xl); + } + + h2 { + font-size: var(--font-size-lg); + } +} + +@media (max-width: 40rem) { + body { + padding: 0 var(--spacing-sm); + } + + h1 { + flex-direction: column; + align-items: flex-start; + gap: var(--spacing-xs); + } +} diff --git a/server/site/style.css b/server/site/style.css index cff2bec..277046b 100644 --- a/server/site/style.css +++ b/server/site/style.css @@ -1,100 +1,650 @@ +/* ============================================================================= + FuzzyWorld Site Styles + + This file contains site-specific styles for code.fuzzyworld.net and + apps.fuzzyworld.net. It builds upon the shared design system in _shared.css. + + Import order: + 1. _shared.css (design tokens and base styles) + 2. _icons.css (icon system) + 3. style.css (this file - site-specific styles) + ============================================================================= */ + +/* Import shared design system */ +@import url("_shared.css"); +@import url("_icons.css"); + +/* ============================================================================= + Site Configuration + ============================================================================= */ + +/* Site-specific color overrides (if needed) */ :root { - --bg: #ffffff; --fg: #1d1d1f; --muted: #62626a; --line: #e1e1e6; --soft: #f6f6f8; --card: #ffffff; --accent-soft: #e8f0fe; - --radius: 8px; --shadow: 0 1px 2px rgb(0 0 0 / 6%); - --link: #0a58ca; --add: #e6f4ea; --add-fg: #116329; --del: #fdecea; --del-fg: #a40e26; --hunk: #6f42c1; + --site-bg: var(--color-bg-primary); + --site-fg: var(--color-fg-primary); + --site-muted: var(--color-fg-muted); + --site-line: var(--color-border-primary); + --site-soft: var(--color-bg-secondary); + --site-card: var(--color-bg-tertiary); + --site-accent-soft: var(--color-accent-soft); + + --site-link: var(--color-accent); + + --site-add: var(--color-success-bg); + --site-add-fg: var(--color-success); + --site-del: var(--color-error-bg); + --site-del-fg: var(--color-error); + --site-hunk: var(--color-warning); } + @media (prefers-color-scheme: dark) { :root { - --bg: #111113; --fg: #e8e8ea; --muted: #9a9aa2; --line: #2a2a2f; --soft: #1a1a1e; --card: #17171a; --accent-soft: #16243a; - --shadow: none; - --link: #79b8ff; --add: #12261a; --add-fg: #7ee2a8; --del: #2d1517; --del-fg: #ff9492; --hunk: #c5a5ff; + --site-add: var(--color-success-bg); + --site-add-fg: var(--color-success); + --site-del: var(--color-error-bg); + --site-del-fg: var(--color-error); + --site-hunk: var(--color-warning); } } -:root { color-scheme: light dark; } -* { box-sizing: border-box; } -html { -webkit-text-size-adjust: 100%; } -body { margin: 0; background: var(--bg); color: var(--fg); font: 16px/1.55 system-ui, -apple-system, "Segoe UI", sans-serif; } -a { color: var(--link); text-decoration: none; } -a:hover { text-decoration: underline; } -main { max-width: 72rem; margin: 0 auto; padding: 0 16px 3rem; } -main > p, main > ul:not(.repos), main > ol { max-width: 46rem; } -:focus-visible { outline: 2px solid var(--link); outline-offset: 2px; border-radius: 3px; } -/* Site header and footer: the same links on every page of both sites (gen.py NAV). */ -header.site { background: var(--soft); border-bottom: 1px solid var(--line); } -header.site .bar, footer.site .cols { max-width: 72rem; margin: 0 auto; padding: 0 16px; } -header.site .bar { display: flex; flex-wrap: wrap; align-items: center; gap: 0 1.5rem; } -header.site .brand { font-weight: 700; font-size: 1.1rem; color: var(--fg); padding: 0.75rem 0; letter-spacing: -0.01em; } -header.site .brand:hover { text-decoration: none; color: var(--link); } -.site-nav { display: flex; flex-wrap: wrap; gap: 0 1.25rem; } -.site-nav a { color: var(--muted); padding: 0.75rem 0 calc(0.75rem - 2px); border-bottom: 2px solid transparent; } -.site-nav a:hover { color: var(--fg); text-decoration: none; } -.site-nav a[aria-current] { color: var(--fg); font-weight: 600; border-bottom-color: var(--link); } -footer.site { margin-top: 2rem; border-top: 1px solid var(--line); background: var(--soft); color: var(--muted); font-size: 0.9rem; } -footer.site .cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: 1.5rem; padding-top: 1.5rem; padding-bottom: 2rem; } -footer.site h2 { font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); margin: 0 0 0.4rem; } -footer.site a, footer.site span { display: block; padding: 0.15rem 0; } -h1 { font-size: 1.75rem; line-height: 1.25; margin: 1.5rem 0 0.5rem; overflow-wrap: anywhere; letter-spacing: -0.01em; } -h2 { font-size: 1.2rem; margin: 1.75rem 0 0.5rem; } -code, pre, .source, .diff { font: 13px/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; tab-size: 4; } -pre { background: var(--soft); border: 1px solid var(--line); border-radius: var(--radius); padding: 0.75rem; overflow-x: auto; } -.repo-nav { display: flex; flex-wrap: wrap; gap: 0 1.25rem; margin-top: 1rem; border-bottom: 1px solid var(--line); } -.repo-nav a { color: var(--muted); padding: 0.5rem 0 calc(0.5rem - 2px); border-bottom: 2px solid transparent; } -.repo-nav a:hover { color: var(--fg); text-decoration: none; border-bottom-color: var(--line); } -.crumbs { color: var(--muted); overflow-wrap: anywhere; } -.meta, .desc { color: var(--muted); overflow-wrap: anywhere; } -pre.message { white-space: pre-wrap; overflow-wrap: anywhere; } -.clone { user-select: all; } -table { border-collapse: collapse; width: 100%; } -td, th { padding: 0.3rem 0.5rem; border-bottom: 1px solid var(--line); text-align: left; vertical-align: top; } -th { color: var(--muted); font-weight: 500; font-size: 0.9rem; } -table:not(.source) tr:hover > td { background: var(--soft); } -td.num, td.date { white-space: nowrap; color: var(--muted); } -td.date { width: 7rem; } -td.author { color: var(--muted); white-space: nowrap; } -.files td.dir a { font-weight: 600; } -.table-wrap, .source, .log, .index { display: block; overflow-x: auto; } -.source { border: 1px solid var(--line); border-radius: 6px; } -.source td { border: 0; padding: 0 0.5rem; white-space: pre; } -.source td.ln { text-align: right; color: var(--muted); user-select: none; width: 1%; } -.source td.ln a { color: inherit; } -.source tr:target { background: var(--soft); } -.diff .add { background: var(--add); color: var(--add-fg); display: inline-block; min-width: 100%; } -.diff .del { background: var(--del); color: var(--del-fg); display: inline-block; min-width: 100%; } -.diff .hunk { color: var(--hunk); } -.diff .file { font-weight: 700; } -.markdown img, img.blob { max-width: 100%; height: auto; } -.markdown table { display: block; overflow-x: auto; } -.readme { border-top: 1px solid var(--line); margin-top: 1.5rem; } -ul.repos { list-style: none; padding: 0; margin: 1.25rem 0; display: grid; gap: 0.75rem; grid-template-columns: repeat(auto-fill, minmax(min(100%, 21rem), 1fr)); } -ul.repos li { padding: 0.9rem 1rem; background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); } -ul.repos li:hover { border-color: var(--link); } -ul.repos .name { font-weight: 600; font-size: 1.05rem; overflow-wrap: anywhere; } -ul.repos .meta { display: block; font-size: 0.85rem; } -ul.repos .desc { margin: 0.35rem 0 0; font-size: 0.95rem; } -details summary { cursor: pointer; color: var(--muted); margin: 1rem 0 0.5rem; } -.branch-note { background: var(--soft); border: 1px solid var(--line); border-radius: 6px; padding: 0.4rem 0.75rem; overflow-wrap: anywhere; } -/* Blame: who last changed each run of lines. */ -.source.blamed td.blame { white-space: nowrap; color: var(--muted); font-size: 12px; border-right: 1px solid var(--line); min-width: 18rem; } -.source.blamed tr.run td { border-top: 1px solid var(--line); } -.source.blamed .who { display: inline-block; max-width: 9rem; overflow: hidden; text-overflow: ellipsis; vertical-align: bottom; } -/* CI */ -.ci { font-weight: 700; text-decoration: none; } -.ci-success { color: var(--add-fg); } .ci-failed { color: var(--del-fg); } .ci-running { color: var(--hl-m); } .ci-error { color: var(--muted); } -pre.ci-log { max-height: 40rem; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; } -/* Syntax highlighting (class names from server/gen.py token_class). */ -:root { --hl-c: #6a737d; --hl-k: #cf222e; --hl-s: #0a3069; --hl-m: #0550ae; --hl-nf: #8250df; --hl-nc: #953800; --hl-nt: #116329; } + +/* ============================================================================= + Base Overrides + ============================================================================= */ + +/* Ensure color-scheme is set */ +:root { + color-scheme: light dark; +} + +html { + -webkit-text-size-adjust: 100%; +} + +body { + margin: 0; + background: var(--site-bg); + color: var(--site-fg); + font: 16px/1.55 var(--font-family-sans); +} + +a { + color: var(--site-link); + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +/* ============================================================================= + Site Header & Footer + ============================================================================= */ + +header.site { + background: var(--site-soft); + border-bottom: 1px solid var(--site-line); +} + +header.site .bar, +footer.site .cols { + max-width: 72rem; + margin: 0 auto; + padding: 0 var(--spacing-md); +} + +header.site .bar { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0 var(--spacing-lg); +} + +header.site .brand { + font-weight: var(--font-weight-bold); + font-size: var(--font-size-lg); + color: var(--site-fg); + padding: var(--spacing-sm) 0; + letter-spacing: -0.01em; +} + +header.site .brand:hover { + text-decoration: none; + color: var(--site-link); +} + +.site-nav { + display: flex; + flex-wrap: wrap; + gap: 0 var(--spacing-md); +} + +.site-nav a { + color: var(--site-muted); + padding: var(--spacing-sm) 0 calc(var(--spacing-sm) - 2px); + border-bottom: 2px solid transparent; +} + +.site-nav a:hover { + color: var(--site-fg); + text-decoration: none; +} + +.site-nav a[aria-current] { + color: var(--site-fg); + font-weight: var(--font-weight-semibold); + border-bottom-color: var(--site-link); +} + +/* Site icon in header */ +header.site .brand::before { + content: ""; + display: inline-block; + width: 1.1em; + height: 1.1em; + margin-right: 0.3em; + vertical-align: -0.1em; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%230a58ca'%3E%3Cpath d='M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5'/%3E%3C/svg%3E"); + background-size: contain; + background-repeat: no-repeat; + background-position: center; +} + @media (prefers-color-scheme: dark) { - :root { --hl-c: #8b949e; --hl-k: #ff7b72; --hl-s: #a5d6ff; --hl-m: #79c0ff; --hl-nf: #d2a8ff; --hl-nc: #ffa657; --hl-nt: #7ee787; } + header.site .brand::before { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%2379b8ff'%3E%3Cpath d='M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5'/%3E%3C/svg%3E"); + } } -.code .c { color: var(--hl-c); font-style: italic; } .code .cp, .code .k { color: var(--hl-k); } -.code .s { color: var(--hl-s); } .code .m, .code .nb, .code .na { color: var(--hl-m); } -.code .nf, .code .nd { color: var(--hl-nf); } .code .nc { color: var(--hl-nc); } .code .nt { color: var(--hl-nt); } -.code .gi { color: var(--add-fg); } .code .gd { color: var(--del-fg); } .code .gh { font-weight: 700; } -@media (max-width: 40rem) { - td.author, .log td.date { display: none; } + +footer.site { + margin-top: var(--spacing-xl); + border-top: 1px solid var(--site-line); + background: var(--site-soft); + color: var(--site-muted); + font-size: var(--font-size-sm); +} + +footer.site .cols { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); + gap: var(--spacing-lg); + padding-top: var(--spacing-lg); + padding-bottom: var(--spacing-xl); +} + +footer.site h2 { + font-size: var(--font-size-xs); + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--site-muted); + margin: 0 0 var(--spacing-xs); +} + +footer.site a, +footer.site span { + display: block; + padding: var(--spacing-xs) 0; +} + +/* ============================================================================= + Main Content Layout + ============================================================================= */ + +main { + max-width: 72rem; + margin: 0 auto; + padding: 0 var(--spacing-md) var(--spacing-xl); +} + +main > p, +main > ul:not(.repos), +main > ol { + max-width: 46rem; +} + +/* ============================================================================= + Typography + ============================================================================= */ + +h1 { + font-size: var(--font-size-2xl); + line-height: var(--line-height-tight); + margin: var(--spacing-lg) 0 var(--spacing-md); + overflow-wrap: anywhere; + letter-spacing: -0.01em; +} + +h2 { + font-size: var(--font-size-xl); + margin: var(--spacing-xl) 0 var(--spacing-md); +} + +code, pre, .source, .diff { + font: 13px/1.5 var(--font-family-mono); + tab-size: 4; +} + +pre { + background: var(--site-soft); + border: 1px solid var(--site-line); + border-radius: var(--border-radius-md); + padding: var(--spacing-sm); + overflow-x: auto; +} + +/* ============================================================================= + Navigation + ============================================================================= */ + +.repo-nav { + display: flex; + flex-wrap: wrap; + gap: 0 var(--spacing-md); + margin-top: var(--spacing-md); + border-bottom: 1px solid var(--site-line); +} + +.repo-nav a { + color: var(--site-muted); + padding: var(--spacing-xs) 0 calc(var(--spacing-xs) - 2px); + border-bottom: 2px solid transparent; +} + +.repo-nav a:hover { + color: var(--site-fg); + text-decoration: none; + border-bottom-color: var(--site-line); +} + +/* Breadcrumb navigation */ +.crumbs { + color: var(--site-muted); + overflow-wrap: anywhere; +} + +/* ============================================================================= + Metadata & Descriptions + ============================================================================= */ + +.meta, .desc { + color: var(--site-muted); + overflow-wrap: anywhere; +} + +pre.message { + white-space: pre-wrap; + overflow-wrap: anywhere; +} + +.clone { + user-select: all; +} + +/* ============================================================================= + Tables + ============================================================================= */ + +table { + border-collapse: collapse; + width: 100%; +} + +td, th { + padding: var(--spacing-xs) var(--spacing-sm); + border-bottom: 1px solid var(--site-line); + text-align: left; + vertical-align: top; +} + +th { + color: var(--site-muted); + font-weight: var(--font-weight-medium); + font-size: var(--font-size-sm); +} + +table:not(.source) tr:hover > td { + background: var(--site-soft); +} + +td.num, td.date { + white-space: nowrap; + color: var(--site-muted); +} + +td.date { + width: 7rem; +} + +td.author { + color: var(--site-muted); + white-space: nowrap; +} + +/* File listing table */ +.files td.dir a { + font-weight: var(--font-weight-semibold); +} + +/* ============================================================================= + Code Display + ============================================================================= */ + +.table-wrap, .source, .log, .index { + display: block; + overflow-x: auto; } + +.source { + border: 1px solid var(--site-line); + border-radius: var(--border-radius-md); +} + +.source td { + border: 0; + padding: 0 var(--spacing-sm); + white-space: pre; +} + +.source td.ln { + text-align: right; + color: var(--site-muted); + user-select: none; + width: 1%; +} + +.source td.ln a { + color: inherit; +} + +.source tr:target { + background: var(--site-soft); +} + +/* Diff styling */ +.diff .add { + background: var(--site-add); + color: var(--site-add-fg); + display: inline-block; + min-width: 100%; +} + +.diff .del { + background: var(--site-del); + color: var(--site-del-fg); + display: inline-block; + min-width: 100%; +} + +.diff .hunk { + color: var(--site-hunk); +} + +.diff .file { + font-weight: var(--font-weight-bold); +} + +/* Markdown content */ +.markdown img, +img.blob { + max-width: 100%; + height: auto; +} + +.markdown table { + display: block; + overflow-x: auto; +} + +.readme { + border-top: 1px solid var(--site-line); + margin-top: var(--spacing-xl); +} + +/* ============================================================================= + Repository/App Cards + ============================================================================= */ + +ul.repos { + list-style: none; + padding: 0; + margin: var(--spacing-md) 0; + display: grid; + gap: var(--gap-sm); + grid-template-columns: repeat(auto-fill, minmax(min(100%, 21rem), 1fr)); +} + +ul.repos li { + padding: var(--spacing-md) var(--spacing-md); + background: var(--site-card); + border: 1px solid var(--site-line); + border-radius: var(--border-radius-lg); + box-shadow: var(--box-shadow); +} + +ul.repos li:hover { + border-color: var(--site-link); +} + +ul.repos .name { + font-weight: var(--font-weight-semibold); + font-size: var(--font-size-lg); + overflow-wrap: anywhere; +} + +ul.repos .meta { + display: block; + font-size: var(--font-size-xs); +} + +ul.repos .desc { + margin: var(--spacing-xs) 0 0; + font-size: var(--font-size-sm); +} + +/* Details/summary styling */ +details summary { + cursor: pointer; + color: var(--site-muted); + margin: var(--spacing-md) 0 var(--spacing-xs); +} + +/* Branch note styling */ +.branch-note { + background: var(--site-soft); + border: 1px solid var(--site-line); + border-radius: var(--border-radius-md); + padding: var(--spacing-xs) var(--spacing-sm); + overflow-wrap: anywhere; +} + +/* ============================================================================= + Blame View + ============================================================================= */ + +/* Blame: who last changed each run of lines. */ +.source.blamed td.blame { + white-space: nowrap; + color: var(--site-muted); + font-size: 0.75rem; + border-right: 1px solid var(--site-line); + min-width: 18rem; +} + +.source.blamed tr.run td { + border-top: 1px solid var(--site-line); +} + +.source.blamed .who { + display: inline-block; + max-width: 9rem; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: bottom; +} + +/* ============================================================================= + CI Status + ============================================================================= */ + +.ci { + font-weight: var(--font-weight-bold); + text-decoration: none; +} + +.ci-success { color: var(--site-add-fg); } +.ci-failed { color: var(--site-del-fg); } +.ci-running { color: var(--site-hunk); } +.ci-error { color: var(--site-muted); } + +pre.ci-log { + max-height: 40rem; + overflow: auto; + white-space: pre-wrap; + overflow-wrap: anywhere; +} + +/* ============================================================================= + Syntax Highlighting + ============================================================================= */ + +:root { + --hl-c: var(--color-hl-comment); + --hl-k: var(--color-hl-keyword); + --hl-s: var(--color-hl-string); + --hl-m: var(--color-hl-number); + --hl-nf: var(--color-hl-function); + --hl-nc: var(--color-hl-class); + --hl-nt: var(--color-hl-type); +} + +.code .c { color: var(--hl-c); font-style: italic; } +.code .cp, .code .k { color: var(--hl-k); } +.code .s { color: var(--hl-s); } +.code .m, .code .nb, .code .na { color: var(--hl-m); } +.code .nf, .code .nd { color: var(--hl-nf); } +.code .nc { color: var(--hl-nc); } +.code .nt { color: var(--hl-nt); } +.code .gi { color: var(--site-add-fg); } +.code .gd { color: var(--site-del-fg); } +.code .gh { font-weight: var(--font-weight-bold); } + +/* ============================================================================= + Screenshots + ============================================================================= */ + /* Screenshots: thumbnails on the apps index, a row on a repo's summary page. */ -ul.repos li.has-shot { display: grid; grid-template-columns: 5.5rem 1fr; gap: 0.9rem; align-items: start; } -ul.repos .thumb img { display: block; width: 5.5rem; height: 7.5rem; object-fit: contain; object-position: left top; border: 1px solid var(--line); border-radius: 6px; background: var(--soft); } -.shots { display: flex; gap: 0.75rem; overflow-x: auto; } -.shots img { display: block; height: 16rem; width: auto; border: 1px solid var(--line); border-radius: var(--radius); } +ul.repos li.has-shot { + display: grid; + grid-template-columns: 5.5rem 1fr; + gap: var(--gap-sm); + align-items: start; +} + +ul.repos .thumb img { + display: block; + width: 5.5rem; + height: 7.5rem; + object-fit: contain; + object-position: left top; + border: 1px solid var(--site-line); + border-radius: var(--border-radius-md); + background: var(--site-soft); +} + +.shots { + display: flex; + gap: var(--gap-sm); + overflow-x: auto; +} + +.shots img { + display: block; + height: 16rem; + width: auto; + border: 1px solid var(--site-line); + border-radius: var(--border-radius-lg); +} + +/* ============================================================================= + Responsive Adjustments + ============================================================================= */ + +@media (max-width: 40rem) { + td.author, + .log td.date { + display: none; + } +} + +/* ============================================================================= + Icon Integration + ============================================================================= */ + +/* VS Code links get the VS Code icon automatically */ +a[href*="vscode://vscode.git/clone"]::before, +a[href*="/vscode/" i]::before { + content: ""; + display: inline-block; + width: 0.9em; + height: 0.9em; + margin-right: 0.35em; + vertical-align: -0.1em; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='currentColor' d='M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5'/%3E%3C/svg%3E"); + background-size: contain; + background-repeat: no-repeat; + background-position: center; +} + +/* Clone commands get a git icon */ +pre.clone::before { + content: "$ "; + color: var(--site-muted); +} + +/* App badges for repo cards */ +.repo-has-app::after { + content: "app"; + margin-left: var(--spacing-xs); + padding: 0 var(--spacing-xs); + background: var(--site-soft); + border-radius: var(--border-radius-sm); + font-size: 0.8em; + color: var(--site-muted); +} + +/* ============================================================================= + Badge Styles + ============================================================================= */ + +.badge { + display: inline-block; + padding: var(--spacing-xs) var(--spacing-sm); + background: var(--site-soft); + color: var(--site-muted); + border: 1px solid var(--site-line); + border-radius: var(--border-radius-full); + font-size: var(--font-size-xs); +} + +.badge-success { + background: var(--site-add); + color: var(--site-add-fg); + border-color: var(--site-add-fg); +} + +.badge-error { + background: var(--site-del); + color: var(--site-del-fg); + border-color: var(--site-del-fg); +} + +.badge-warning { + background: var(--color-warning-bg); + color: var(--color-warning); + border-color: var(--color-warning); +} + +.badge-info { + background: var(--site-accent-soft); + color: var(--color-accent); + border-color: var(--color-accent); +}