diff --git a/assets/app.ts b/assets/app.ts index f69ebe1..7e55b54 100644 --- a/assets/app.ts +++ b/assets/app.ts @@ -5,5 +5,7 @@ * (and its CSS file) in your base layout (base.html.twig). */ +// require('bootstrap'); + // any CSS you import will output into a single css file (app.scss in this case) import './styles/app.scss'; diff --git a/assets/styles/app.scss b/assets/styles/app.scss index cb33b13..9f508aa 100644 --- a/assets/styles/app.scss +++ b/assets/styles/app.scss @@ -1,3 +1,379 @@ -body { - background-color: lightgray; +// ============================================================================= +// Design Tokens — SCSS Variables +// ============================================================================= + +// Surface palette +$color-surface: #0b1326; +$color-surface-dim: #0b1326; +$color-surface-bright: #31394d; +$color-surface-container-lowest: #060e20; +$color-surface-container-low: #131b2e; +$color-surface-container: #171f33; +$color-surface-container-high: #222a3d; +$color-surface-container-highest: #2d3449; +$color-on-surface: #dae2fd; +$color-on-surface-variant: #c0c7d3; +$color-inverse-surface: #dae2fd; +$color-inverse-on-surface: #283044; +$color-outline: #8a919d; +$color-outline-variant: #404751; +$color-surface-tint: #9dcaff; +$color-surface-variant: #2d3449; + +// Primary — Electric Azure +$color-primary: #9dcaff; +$color-on-primary: #003257; +$color-primary-container: #50a7fa; +$color-on-primary-container: #003b65; +$color-inverse-primary: #0061a2; + +// Secondary +$color-secondary: #89ceff; +$color-on-secondary: #00344d; +$color-secondary-container: #00a2e6; +$color-on-secondary-container: #00344e; + +// Tertiary (indigo accent) +$color-tertiary: #c0c1ff; +$color-on-tertiary: #1000a9; +$color-tertiary-container: #9598ff; +$color-on-tertiary-container: #1c16b1; + +// Error +$color-error: #ffb4ab; +$color-on-error: #690005; +$color-error-container: #93000a; +$color-on-error-container: #ffdad6; + +// Background +$color-background: #0b1326; +$color-on-background: #dae2fd; + +// Typography stacks +$font-display: 'Space Grotesk', system-ui, sans-serif; +$font-body: 'Geist', system-ui, -apple-system, sans-serif; +$font-mono: 'JetBrains Mono', 'Courier New', monospace; + +// Border radius scale +$radius-sm: 0.25rem; +$radius-default: 0.5rem; +$radius-md: 0.75rem; +$radius-lg: 1rem; +$radius-xl: 1.5rem; +$radius-full: 9999px; + +// ============================================================================= +// Bootstrap Variable Overrides +// Must be declared before @import for !default overrides to take effect. +// Bootstrap 5.x uses @import internally — @use with() is not supported. +// ============================================================================= + +// Semantic color map +$primary: $color-primary-container; // #50a7fa — Electric Azure +$secondary: $color-secondary; // #89ceff +$success: #4ade80; +$info: $color-primary; // #9dcaff +$warning: #fbbf24; +$danger: $color-error; // #ffb4ab +$light: $color-surface-bright; // #31394d +$dark: $color-surface-container-lowest; // #060e20 + +// Body +$body-bg: $color-background; // #0b1326 +$body-color: $color-on-surface; // #dae2fd +$body-secondary-color: $color-on-surface-variant; // #c0c7d3 + +// Links +$link-color: $color-primary; // #9dcaff +$link-hover-color: $color-primary-container; // #50a7fa + +// Borders +$border-color: $color-outline-variant; // #404751 + +// Border radius +$border-radius: $radius-default; // 0.5rem +$border-radius-sm: $radius-sm; // 0.25rem +$border-radius-lg: $radius-md; // 0.75rem +$border-radius-xl: $radius-lg; // 1rem +$border-radius-xxl: $radius-xl; // 1.5rem +$border-radius-pill: $radius-full; // 9999px + +// Typography +$font-family-sans-serif: $font-body; +$font-family-monospace: $font-mono; +$font-size-base: 1rem; // 16px +$line-height-base: 1.5; + +// Headings +$headings-font-family: $font-display; +$headings-font-weight: 600; +$headings-line-height: 1.3; +$headings-color: $color-on-surface; + +// Grid +$grid-gutter-width: 1.5rem; // 24px +$container-max-widths: ( + sm: 540px, + md: 720px, + lg: 960px, + xl: 1140px, + xxl: 1280px, +); + +// Cards +$card-bg: $color-surface-container; +$card-border-color: $color-outline-variant; +$card-cap-bg: transparent; + +// Form inputs +$input-bg: $color-surface-container-low; +$input-border-color: $color-outline-variant; +$input-color: $color-on-surface; +$input-focus-border-color: $color-primary-container; +$input-placeholder-color: $color-on-surface-variant; + +// Code +$code-color: $color-tertiary; +$pre-color: $color-on-surface; + +// Navbar +$navbar-dark-color: rgba($color-on-surface, 0.85); +$navbar-dark-hover-color: $color-primary; +$navbar-dark-active-color: $color-primary; + +// ============================================================================= +// Bootstrap Import +// ============================================================================= + +@import '~bootstrap/scss/bootstrap'; + +// ============================================================================= +// CSS Custom Properties — Full Design Token Set +// ============================================================================= + +:root { + // Surface + --color-surface: #{$color-surface}; + --color-surface-dim: #{$color-surface-dim}; + --color-surface-bright: #{$color-surface-bright}; + --color-surface-container-lowest: #{$color-surface-container-lowest}; + --color-surface-container-low: #{$color-surface-container-low}; + --color-surface-container: #{$color-surface-container}; + --color-surface-container-high: #{$color-surface-container-high}; + --color-surface-container-highest: #{$color-surface-container-highest}; + --color-on-surface: #{$color-on-surface}; + --color-on-surface-variant: #{$color-on-surface-variant}; + --color-inverse-surface: #{$color-inverse-surface}; + --color-inverse-on-surface: #{$color-inverse-on-surface}; + --color-outline: #{$color-outline}; + --color-outline-variant: #{$color-outline-variant}; + --color-surface-tint: #{$color-surface-tint}; + --color-surface-variant: #{$color-surface-variant}; + + // Primary + --color-primary: #{$color-primary}; + --color-on-primary: #{$color-on-primary}; + --color-primary-container: #{$color-primary-container}; + --color-on-primary-container: #{$color-on-primary-container}; + --color-inverse-primary: #{$color-inverse-primary}; + + // Secondary + --color-secondary: #{$color-secondary}; + --color-on-secondary: #{$color-on-secondary}; + --color-secondary-container: #{$color-secondary-container}; + --color-on-secondary-container: #{$color-on-secondary-container}; + + // Tertiary + --color-tertiary: #{$color-tertiary}; + --color-on-tertiary: #{$color-on-tertiary}; + --color-tertiary-container: #{$color-tertiary-container}; + --color-on-tertiary-container: #{$color-on-tertiary-container}; + + // Error + --color-error: #{$color-error}; + --color-on-error: #{$color-on-error}; + --color-error-container: #{$color-error-container}; + --color-on-error-container: #{$color-on-error-container}; + + // Background + --color-background: #{$color-background}; + --color-on-background: #{$color-on-background}; + + // Font stacks + --font-display: #{$font-display}; + --font-body: #{$font-body}; + --font-mono: #{$font-mono}; + + // Spacing scale (4px baseline) + --spacing-xs: 4px; + --spacing-sm: 8px; + --spacing-md: 16px; + --spacing-lg: 24px; + --spacing-xl: 32px; + + // Border radius + --radius-sm: #{$radius-sm}; + --radius: #{$radius-default}; + --radius-md: #{$radius-md}; + --radius-lg: #{$radius-lg}; + --radius-xl: #{$radius-xl}; + --radius-full: #{$radius-full}; + + // Elevation: glow-only focus/hover effect + --glow-primary: 0 0 0 3px rgba(80, 167, 250, 0.2); +} + +// ============================================================================= +// Base Styles +// ============================================================================= + +body { + background-color: var(--color-background); + color: var(--color-on-surface); + font-family: var(--font-body); +} + +h1, h2, h3, h4, h5, h6 { + font-family: var(--font-display); +} + +code, kbd, pre, samp { + font-family: var(--font-mono); +} + +// ============================================================================= +// Typography Utility Classes +// ============================================================================= + +.text-display-lg { + font-family: var(--font-display); + font-size: 48px; + font-weight: 700; + line-height: 1.1; + letter-spacing: -0.02em; + + @media (max-width: 768px) { + font-size: 32px; + line-height: 1.2; + } +} + +.text-headline-md { + font-family: var(--font-display); + font-size: 24px; + font-weight: 600; + line-height: 1.3; +} + +.text-body-lg { + font-family: var(--font-body); + font-size: 18px; + line-height: 1.6; +} + +.text-body-md { + font-family: var(--font-body); + font-size: 16px; + line-height: 1.5; +} + +.text-code-sm { + font-family: var(--font-mono); + font-size: 14px; + line-height: 1.5; +} + +.text-label-caps { + font-family: var(--font-mono); + font-size: 12px; + font-weight: 600; + line-height: 1; + letter-spacing: 0.05em; + text-transform: uppercase; +} + +// ============================================================================= +// Component Overrides +// ============================================================================= + +// Buttons — glow on interactive states +.btn-primary { + color: $color-on-primary-container; + + &:hover, + &:focus-visible { + box-shadow: var(--glow-primary); + } +} + +.btn-outline-primary { + &:hover, + &:focus-visible { + box-shadow: var(--glow-primary); + } +} + +// Cards — tonal layering, no shadows +.card { + box-shadow: none; + + .card-title { + font-family: var(--font-display); + } +} + +// Inputs — glow on focus (already wired via $input-focus-border-color) +.form-control, +.form-select { + &:focus { + box-shadow: var(--glow-primary); + } +} + +// Tech chips — JetBrains Mono, muted azure fill +.badge-tech { + font-family: var(--font-mono); + font-size: 12px; + font-weight: 600; + letter-spacing: 0.05em; + text-transform: uppercase; + border-radius: var(--radius); + padding: 4px 8px; + background-color: rgba(80, 167, 250, 0.1); + color: var(--color-primary-container); + border: 1px solid rgba(80, 167, 250, 0.2); +} + +// Code blocks — darkest surface, language label top-right +pre { + position: relative; + background-color: var(--color-surface-container-lowest); + border: 1px solid var(--color-outline-variant); + + code { + font-family: var(--font-mono); + font-size: 14px; + } + + .code-lang { + position: absolute; + top: var(--spacing-sm); + right: var(--spacing-md); + font-family: var(--font-mono); + font-size: 12px; + font-weight: 600; + letter-spacing: 0.05em; + text-transform: uppercase; + color: var(--color-on-surface-variant); + } +} + +// Interactive glow helper +.interactive-glow { + transition: box-shadow 0.2s ease; + + &:hover, + &:focus { + box-shadow: var(--glow-primary); + } } diff --git a/package.json b/package.json index d46d7db..1a9f60d 100644 --- a/package.json +++ b/package.json @@ -20,5 +20,9 @@ "dev": "encore dev", "watch": "encore dev --watch", "build": "encore production --progress" + }, + "dependencies": { + "@popperjs/core": "^2.11.8", + "bootstrap": "^5.3.8" } } diff --git a/yarn.lock b/yarn.lock index 9c2a965..c35acc6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -972,6 +972,11 @@ "@parcel/watcher-win32-ia32" "2.5.6" "@parcel/watcher-win32-x64" "2.5.6" +"@popperjs/core@^2.11.8": + version "2.11.8" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" + integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== + "@sinclair/typebox@^0.34.0": version "0.34.49" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.34.49.tgz#4f1369234f2ecf693866476c3b2e1b54d2a9d68e" @@ -1323,6 +1328,11 @@ boolbase@^1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== +bootstrap@^5.3.8: + version "5.3.8" + resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.3.8.tgz#6401a10057a22752d21f4e19055508980656aeed" + integrity sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg== + brace-expansion@^1.1.7: version "1.1.15" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.15.tgz#a6d90d54067236e5f42570a3b7378d594d9b7738"