diff --git a/documentation/design-tokens.css b/documentation/design-tokens.css new file mode 100644 index 00000000..bedd6f4a --- /dev/null +++ b/documentation/design-tokens.css @@ -0,0 +1,954 @@ +/** + * Y STEM and Chess — Design Tokens + * ============================================================ + * Standalone CSS stylesheet for use by AI coding agents and any + * environment that does NOT have access to the Tailwind CSS build. + * + * No build step required. Link directly: + * + * + * Or reference the raw GitHub URL for external agents. + * + * Mirrors the design token system in tailwind.config.js and index.css. + * See design.md for usage rules and anti-patterns. + * ============================================================ + */ + + +/* ============================================================ + SECTION 1: CSS Custom Properties (Design Tokens) + ============================================================ */ + +:root { + + /* ---------------------------------------------------------- + 1.1 Color Palette + Source: tailwind.config.js → theme.extend.colors + ---------------------------------------------------------- */ + + /* Brand greens */ + --color-primary: #7FCC26; /* Main brand green — CTAs, active states, focus rings */ + --color-secondary: #BFD99E; /* Muted green — hover states on icons, supporting accents */ + --color-soft: #E5F3D2; /* Light green — page background, icon containers, card tints */ + + /* Accent */ + --color-accent: #EAD94C; /* Yellow — gamification highlights, active toolbar icons, badge borders */ + + /* Neutrals */ + --color-dark: #1F1F1F; /* Near-black — primary text, borders, btn-primary background */ + --color-gray: #5C5C5C; /* Secondary text — body copy, nav links, dropdown items */ + --color-muted: #8A8A8A; /* Placeholder text, disabled states, metadata labels */ + --color-border-light: #D6D6D6; /* Borders, dividers */ + --color-light: #F9FAF7; /* Off-white — surfaces: navbar, footer, forms, modals, dropdowns */ + + /* Semantic / Error */ + --color-red: #D64545; /* Errors, destructive actions, invalid input borders */ + --color-red-light: #F5E9E9; /* Error backgrounds */ + + + /* ---------------------------------------------------------- + 1.2 Typography + Source: Google Fonts — Lato (400, 500, 700) + ---------------------------------------------------------- */ + + --font-family-base: 'Lato', system-ui, -apple-system, BlinkMacSystemFont, sans-serif; + + --font-weight-normal: 400; + --font-weight-medium: 500; + --font-weight-bold: 700; + + /* Type scale — pixel equivalents of Tailwind's rem scale */ + --text-xs: 0.75rem; /* 12px — captions, metadata, copyright */ + --text-sm: 0.875rem; /* 14px — labels, timestamps, small body */ + --text-base: 1rem; /* 16px — default body text */ + --text-lg: 1.125rem; /* 18px — nav links, slightly larger body */ + --text-xl: 1.25rem; /* 20px — large body, card descriptions */ + --text-2xl: 1.5rem; /* 24px — footer wordmark, sub-headings */ + --text-3xl: 1.875rem; /* 30px — section headings */ + --text-4xl: 2.25rem; /* 36px — hero headings (desktop) */ + + /* Line heights */ + --leading-tight: 1.25; + --leading-normal: 1.5; + --leading-relaxed: 1.625; /* Default for body text blocks */ + + + /* ---------------------------------------------------------- + 1.3 Spacing Scale (4px base) + Mirrors Tailwind's default spacing scale + ---------------------------------------------------------- */ + + --space-0: 0; + --space-1: 0.25rem; /* 4px */ + --space-2: 0.5rem; /* 8px */ + --space-3: 0.75rem; /* 12px */ + --space-4: 1rem; /* 16px */ + --space-5: 1.25rem; /* 20px */ + --space-6: 1.5rem; /* 24px */ + --space-8: 2rem; /* 32px */ + --space-10: 2.5rem; /* 40px */ + --space-12: 3rem; /* 48px */ + --space-16: 4rem; /* 64px */ + --space-20: 5rem; /* 80px */ + --space-24: 6rem; /* 96px */ + + + /* ---------------------------------------------------------- + 1.4 Border Radius + ---------------------------------------------------------- */ + + --radius-sm: 0.125rem; /* 2px — rarely used */ + --radius-md: 0.375rem; /* 6px — small elements */ + --radius-lg: 0.5rem; /* 8px — rounded-lg — dropdowns, icon containers */ + --radius-xl: 0.75rem; /* 12px — rounded-xl — btn-green, inputs */ + --radius-2xl: 1rem; /* 16px — rounded-2xl — modals, forms */ + --radius-3xl: 1.5rem; /* 24px — rounded-3xl — brand tier cards */ + --radius-full: 9999px; /* Full pill — btn-primary, avatar circles, dots */ + + + /* ---------------------------------------------------------- + 1.5 Shadows + Source: tailwind.config.js → theme.extend.boxShadow + ---------------------------------------------------------- */ + + --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); + --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + + /* Brand card shadows — the most visually distinctive pattern */ + --shadow-card-yellow: 1.25rem 1.25rem 0.063rem rgb(209, 230, 28); /* Free card — offset yellow */ + --shadow-card-green: 1.25rem 1.25rem 0.063rem rgb(115, 179, 19); /* Premium card — offset green */ + + + /* ---------------------------------------------------------- + 1.6 Animation + ---------------------------------------------------------- */ + + --duration-fast: 150ms; /* Modals, snappy micro-interactions */ + --duration-base: 300ms; /* Nav links, dropdowns, card hovers */ + --duration-slow: 500ms; /* Button color transitions */ + + --easing-default: ease; + --easing-out: ease-out; + + + /* ---------------------------------------------------------- + 1.7 Z-Index Scale + ---------------------------------------------------------- */ + + --z-base: 0; + --z-above: 10; + --z-dropdown: 20; /* Dropdowns and nav menus */ + --z-sticky: 50; /* Sticky navbar */ + --z-modal: 50; /* Modals and overlays */ +} + + +/* ============================================================ + SECTION 2: Base Reset & Body Defaults + Mirrors src/index.css base styles + ============================================================ */ + +*, +*::before, +*::after { + box-sizing: border-box; +} + +html, +body { + margin: 0; + padding: 0; +} + +body { + font-family: var(--font-family-base); + font-size: var(--text-base); + line-height: var(--leading-normal); + color: var(--color-dark); + background-color: var(--color-soft); +} + +a { + text-decoration: none; + color: inherit; +} + +button { + font-family: inherit; + cursor: pointer; +} + + +/* ============================================================ + SECTION 3: Component Classes + Mirrors @layer components in src/index.css + ============================================================ */ + +/* ---------------------------------------------------------- + 3.1 Button: Primary + Dark pill CTA. Use on light/soft backgrounds. + Example: "Donate", "Join Now!", "Get Started!" + ---------------------------------------------------------- */ +.btn-primary { + display: inline-flex; + align-items: center; + justify-content: center; + + background-color: var(--color-dark); + color: var(--color-light); + font-family: var(--font-family-base); + font-size: var(--text-base); + font-weight: var(--font-weight-bold); /* semibold approximated as bold */ + line-height: var(--leading-relaxed); + + padding: var(--space-3) var(--space-8); + border-radius: var(--radius-full); + border: 2px solid var(--color-dark); + + cursor: pointer; + transition: + transform var(--duration-slow) var(--easing-default), + background-color var(--duration-slow) var(--easing-default); +} + +.btn-primary:hover { + transform: scale(1.05); + background-color: #000; +} + +.btn-primary:active { + transform: scale(0.95); +} + +.btn-primary:focus-visible { + outline: none; + box-shadow: 0 0 0 2px var(--color-primary); +} + + +/* ---------------------------------------------------------- + 3.2 Button: Green + Primary brand green rounded button. Use inside forms, modals. + Example: "Enter", "OK", "Confirm" + ---------------------------------------------------------- */ +.btn-green { + display: inline-flex; + align-items: center; + justify-content: center; + + background-color: var(--color-primary); + color: var(--color-light); + font-family: var(--font-family-base); + font-size: var(--text-xl); + font-weight: var(--font-weight-bold); + + padding: var(--space-3) var(--space-8); + border-radius: var(--radius-xl); + border: none; + box-shadow: var(--shadow-md); + + cursor: pointer; + transition: + opacity var(--duration-base) var(--easing-default), + transform var(--duration-base) var(--easing-default); +} + +.btn-green:hover { + opacity: 0.9; + transform: scale(1.05); +} + +.btn-green:active { + transform: scale(0.95); +} + +.btn-green:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.btn-green:focus-visible { + outline: none; + box-shadow: 0 0 0 2px rgba(127, 204, 38, 0.5); +} + + +/* ---------------------------------------------------------- + 3.3 Button: Toolbar + Transparent icon-only button for the student dashboard toolbar. + Must contain only an SVG element as a child. + ---------------------------------------------------------- */ +.btn-toolbar { + position: relative; + flex: 1; + max-width: 20rem; + padding: 0; + background-color: transparent; + border: none; + border-radius: var(--radius-lg); + cursor: pointer; + transition: + transform var(--duration-base) var(--easing-default); +} + +.btn-toolbar:hover { + transform: scale(1.05); +} + +.btn-toolbar:active { + transform: scale(0.95); +} + +.btn-toolbar:focus-visible { + outline: none; + box-shadow: 0 0 0 2px var(--color-accent); +} + + +/* ---------------------------------------------------------- + 3.4 Scrollbar: Activity Feed + Custom green scrollbar for the activity timeline container. + ---------------------------------------------------------- */ +.activity-scrollbar { + scrollbar-width: thin; + scrollbar-color: var(--color-primary) var(--color-soft); +} + +.activity-scrollbar::-webkit-scrollbar { + width: 6px; +} + +.activity-scrollbar::-webkit-scrollbar-track { + background: var(--color-soft); +} + +.activity-scrollbar::-webkit-scrollbar-thumb { + background-color: var(--color-primary); + border-radius: var(--radius-full); +} + + +/* ============================================================ + SECTION 4: Layout Utilities + ============================================================ */ + +/* ---------------------------------------------------------- + 4.1 Page Container + Standard section container with horizontal padding. + ---------------------------------------------------------- */ +.container-page { + width: 100%; + max-width: 80rem; /* max-w-7xl = 1280px */ + margin-left: auto; + margin-right: auto; + padding-left: var(--space-6); + padding-right: var(--space-6); +} + +@media (min-width: 768px) { + .container-page { + padding-left: var(--space-8); + padding-right: var(--space-8); + } +} + +/* ---------------------------------------------------------- + 4.2 Dashboard Container + Wider container for full-width dashboard layouts. + ---------------------------------------------------------- */ +.container-dashboard { + width: 100%; + max-width: 96rem; /* max-w-screen-2xl = 1536px */ + margin-left: auto; + margin-right: auto; + padding-left: var(--space-6); + padding-right: var(--space-6); +} + + +/* ============================================================ + SECTION 5: Card Components + ============================================================ */ + +/* ---------------------------------------------------------- + 5.1 Standard Content Card + Subtle bordered card for list items and content blocks. + ---------------------------------------------------------- */ +.card { + background-color: var(--color-light); + border: 1px solid var(--color-border-light); + border-radius: var(--radius-lg); + padding: var(--space-4); + box-shadow: var(--shadow-sm); + transition: box-shadow var(--duration-base) var(--easing-default); +} + +.card:hover { + box-shadow: var(--shadow-md); +} + + +/* ---------------------------------------------------------- + 5.2 Brand Card — Green (Free tier) + Large rounded card with offset yellow shadow. + ---------------------------------------------------------- */ +.card-brand-green { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + background-color: var(--color-primary); + border-radius: var(--radius-3xl); + box-shadow: var(--shadow-card-yellow); + padding: var(--space-8); + gap: var(--space-4); + + width: 100%; +} + + +/* ---------------------------------------------------------- + 5.3 Brand Card — Light (Premium tier) + Large rounded card with offset green shadow. + ---------------------------------------------------------- */ +.card-brand-light { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + background-color: var(--color-light); + border-radius: var(--radius-3xl); + box-shadow: var(--shadow-card-green); + padding: var(--space-8); + gap: var(--space-4); + + width: 100%; +} + + +/* ---------------------------------------------------------- + 5.4 Auth / Form Card + Centered form panel. Max width 384px (max-w-sm). + ---------------------------------------------------------- */ +.card-form { + width: 100%; + max-width: 24rem; /* 384px */ + background-color: var(--color-light); + border-radius: var(--radius-2xl); + border: 2px solid var(--color-dark); + box-shadow: var(--shadow-md); + padding: var(--space-8); + display: flex; + flex-direction: column; + gap: var(--space-6); +} + + +/* ---------------------------------------------------------- + 5.5 CTA Box + Full-width bordered call-to-action block. + ---------------------------------------------------------- */ +.card-cta { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: var(--space-6); + + border: 4px solid var(--color-primary); + border-radius: var(--radius-lg); + padding: var(--space-12) var(--space-6); +} + + +/* ============================================================ + SECTION 6: Modal Components + ============================================================ */ + +/* ---------------------------------------------------------- + 6.1 Modal Overlay + Full-screen dark backdrop. Centers modal content. + ---------------------------------------------------------- */ +.modal-overlay { + position: fixed; + inset: 0; + z-index: var(--z-modal); + display: flex; + align-items: center; + justify-content: center; + padding: var(--space-4); + background-color: rgba(31, 31, 31, 0.5); /* dark/50 */ +} + + +/* ---------------------------------------------------------- + 6.2 Modal Content Panel + The white card that slides in on top of the overlay. + ---------------------------------------------------------- */ +.modal-content { + background-color: var(--color-light); + width: 100%; + max-width: 24rem; /* 384px */ + border-radius: var(--radius-2xl); + box-shadow: var(--shadow-xl); + padding: var(--space-8); + + display: flex; + flex-direction: column; + align-items: center; + gap: var(--space-5); + + animation: modal-in var(--duration-fast) var(--easing-out); +} + + +/* ---------------------------------------------------------- + 6.3 Modal Icon Containers + ---------------------------------------------------------- */ +.modal-icon-success { + width: 4rem; + height: 4rem; + border-radius: var(--radius-full); + background-color: rgba(127, 204, 38, 0.2); /* primary/20 */ + display: flex; + align-items: center; + justify-content: center; +} + +.modal-icon-error { + width: 4rem; + height: 4rem; + border-radius: var(--radius-full); + background-color: var(--color-red-light); + display: flex; + align-items: center; + justify-content: center; +} + +.modal-spinner { + width: 4rem; + height: 4rem; + border-radius: var(--radius-full); + border: 4px solid var(--color-soft); + border-top-color: var(--color-primary); + animation: spin 1s linear infinite; +} + + +/* ============================================================ + SECTION 7: Form Elements + ============================================================ */ + +/* ---------------------------------------------------------- + 7.1 Standard Input + Rounded input with brand focus state. + ---------------------------------------------------------- */ +.input-base { + width: 100%; + border-radius: var(--radius-xl); + border: 2px solid var(--color-border-light); + padding: var(--space-3) var(--space-4); + font-family: var(--font-family-base); + font-size: var(--text-sm); + color: var(--color-dark); + background-color: #fff; + caret-color: var(--color-dark); + transition: border-color var(--duration-base) var(--easing-default); +} + +.input-base:focus { + outline: none; + border-color: var(--color-primary); +} + + +/* ---------------------------------------------------------- + 7.2 Input — Error State + ---------------------------------------------------------- */ +.input-error { + border-color: var(--color-red); +} + +.input-error:focus { + border-color: var(--color-red); +} + + +/* ---------------------------------------------------------- + 7.3 Form Label + ---------------------------------------------------------- */ +.label { + display: block; + font-size: var(--text-sm); + font-weight: var(--font-weight-bold); + color: var(--color-dark); + margin-bottom: var(--space-1); +} + + +/* ---------------------------------------------------------- + 7.4 Error / Alert Text + ---------------------------------------------------------- */ +.error-text { + color: var(--color-red); + font-weight: var(--font-weight-bold); + font-size: var(--text-sm); + text-align: center; +} + + +/* ============================================================ + SECTION 8: Navigation + ============================================================ */ + +/* ---------------------------------------------------------- + 8.1 NavBar Shell + Sticky top navigation bar. + ---------------------------------------------------------- */ +.navbar { + background-color: var(--color-light); + border-bottom: 2px solid var(--color-dark); + position: sticky; + top: 0; + z-index: var(--z-sticky); + height: 6rem; /* h-24 */ +} + +/* ---------------------------------------------------------- + 8.2 Nav Link + Standard navigation link — text, hover to primary. + ---------------------------------------------------------- */ +.nav-link { + padding: 0 var(--space-4); + font-size: var(--text-lg); + font-weight: var(--font-weight-medium); + color: var(--color-dark); + transition: color var(--duration-base) var(--easing-default); + text-decoration: none; +} + +.nav-link:hover { + color: var(--color-primary); +} + +/* ---------------------------------------------------------- + 8.3 Dropdown Panel + Floating dropdown menu for nav sections. + ---------------------------------------------------------- */ +.nav-dropdown { + position: absolute; + z-index: var(--z-dropdown); + margin-top: var(--space-3); + width: 16rem; /* w-64 */ + border-radius: var(--radius-md); + background-color: var(--color-light); + padding: var(--space-4); + box-shadow: var(--shadow-lg); +} + +.nav-dropdown-header { + font-size: var(--text-base); + font-weight: var(--font-weight-bold); + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--color-dark); + margin-bottom: var(--space-2); +} + +.nav-dropdown-link { + font-size: var(--text-base); + color: var(--color-gray); + transition: color var(--duration-base) var(--easing-default); + text-decoration: none; + display: block; +} + +.nav-dropdown-link:hover { + color: var(--color-primary); +} + + +/* ============================================================ + SECTION 9: Footer + ============================================================ */ + +.footer { + width: 100%; + background-color: var(--color-light); + border-top: 2px solid var(--color-dark); + padding-top: var(--space-10); + padding-bottom: var(--space-8); +} + +.footer-icon-container { + width: 2.5rem; /* 40px */ + height: 2.5rem; + background-color: var(--color-soft); + border-radius: var(--radius-md); + display: flex; + align-items: center; + justify-content: center; + border: 1px solid rgba(127, 204, 38, 0.2); /* primary/20 */ + transition: background-color var(--duration-base), color var(--duration-base); +} + +.footer-icon-container:hover { + background-color: var(--color-primary); + color: var(--color-light); +} + +.footer-social-btn { + padding: var(--space-2); + border-radius: var(--radius-lg); + transition: background-color var(--duration-base) var(--easing-default); + display: inline-flex; +} + +.footer-social-btn:hover { + background-color: var(--color-soft); +} + + +/* ============================================================ + SECTION 10: Activity Timeline + ============================================================ */ + +/* ---------------------------------------------------------- + 10.1 Timeline Container + Scrollable feed with dotted left border. + ---------------------------------------------------------- */ +.timeline-container { + position: relative; + max-height: 700px; + overflow-y: auto; + padding-right: var(--space-2); +} + +/* Apply activity-scrollbar styles */ +.timeline-container { + scrollbar-width: thin; + scrollbar-color: var(--color-primary) var(--color-soft); +} + +/* ---------------------------------------------------------- + 10.2 Timeline Track + The vertical dotted line. + ---------------------------------------------------------- */ +.timeline-track { + position: absolute; + left: 1rem; /* left-4 */ + top: 0; + bottom: 0; + width: 0; + border-left: 3px dotted var(--color-gray); +} + +/* ---------------------------------------------------------- + 10.3 Timeline Dot + Green circle marker for each activity. + ---------------------------------------------------------- */ +.timeline-dot { + position: absolute; + left: -2.25rem; /* -left-9 */ + top: 1.5rem; /* top-6 */ + width: 0.75rem; + height: 0.75rem; + background-color: var(--color-primary); + border-radius: var(--radius-full); + border: 2px solid var(--color-light); + box-shadow: var(--shadow-sm); +} + + +/* ============================================================ + SECTION 11: Loading Spinner + ============================================================ */ + +.spinner { + display: inline-block; + width: 1.25rem; + height: 1.25rem; + border: 3px solid rgba(127, 204, 38, 0.3); + border-top-color: var(--color-primary); + border-radius: var(--radius-full); + animation: spin 1s linear infinite; +} + +.spinner-lg { + width: 4rem; + height: 4rem; + border-width: 4px; +} + + +/* ============================================================ + SECTION 12: Typography Utility Classes + ============================================================ */ + +.text-hero { + font-size: var(--text-3xl); + font-weight: var(--font-weight-bold); + color: var(--color-dark); + line-height: var(--leading-relaxed); +} + +@media (min-width: 768px) { + .text-hero { + font-size: var(--text-4xl); + } +} + +.text-section-heading { + font-size: var(--text-3xl); + font-weight: var(--font-weight-bold); + color: var(--color-dark); + text-align: center; +} + +@media (min-width: 768px) { + .text-section-heading { + font-size: var(--text-4xl); + } +} + +.text-body-large { + font-size: var(--text-xl); + color: var(--color-gray); + line-height: var(--leading-relaxed); +} + +.text-body { + font-size: var(--text-base); + color: var(--color-dark); + line-height: var(--leading-relaxed); +} + +.text-body-secondary { + font-size: var(--text-base); + color: var(--color-gray); + line-height: var(--leading-relaxed); +} + +.text-label { + font-size: var(--text-xs); + font-weight: var(--font-weight-bold); + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--color-muted); +} + +.text-error { + color: var(--color-red); + font-weight: var(--font-weight-bold); +} + +.text-accent-link { + color: var(--color-primary); + text-decoration: underline; + cursor: pointer; + transition: color var(--duration-base) var(--easing-default); +} + +.text-accent-link:hover { + color: var(--color-secondary); +} + + +/* ============================================================ + SECTION 13: Avatar / Profile Picture + ============================================================ */ + +.avatar { + position: relative; + width: 3.5rem; /* 56px */ + height: 3.5rem; + border-radius: var(--radius-full); + background-color: var(--color-light); + border: 2px solid var(--color-primary); + display: flex; + align-items: center; + justify-content: center; + box-shadow: var(--shadow-sm); + cursor: pointer; + transition: transform var(--duration-base) var(--easing-default); +} + +.avatar:hover { + transform: scale(1.05); +} + +.avatar:active { + transform: scale(0.95); +} + + +/* ============================================================ + SECTION 14: Keyframe Animations + Mirrors tailwind.config.js → theme.extend.keyframes + ============================================================ */ + +/* ---------------------------------------------------------- + Modal entry — scale + fade in + Usage: apply animation: modal-in 0.15s ease-out + ---------------------------------------------------------- */ +@keyframes modal-in { + 0% { + opacity: 0; + transform: scale(0.95) translateY(8px); + } + 100% { + opacity: 1; + transform: scale(1) translateY(0); + } +} + +/* ---------------------------------------------------------- + Fade out — for toast/notification dismissal + Usage: apply animation: fade-out 0.4s ease 2.1s forwards + ---------------------------------------------------------- */ +@keyframes fade-out { + to { + opacity: 0; + transform: translateY(-5px); + } +} + +/* ---------------------------------------------------------- + Shake — for form validation errors + Usage: apply animation: shake 0.5s ease + ---------------------------------------------------------- */ +@keyframes shake { + 0%, 100% { transform: translateX(0); } + 25% { transform: translateX(-5px); } + 75% { transform: translateX(5px); } +} + +/* ---------------------------------------------------------- + Spin — for loading states + Usage: apply animation: spin 1s linear infinite + ---------------------------------------------------------- */ +@keyframes spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +/* Convenience animation classes */ +.animate-modal-in { + animation: modal-in 150ms ease-out; +} + +.animate-fade-out { + animation: fade-out 0.4s ease 2.1s forwards; +} + +.animate-shake { + animation: shake 0.5s ease; +} + +.animate-spin { + animation: spin 1s linear infinite; +} diff --git a/documentation/design-tokens.rn.ts b/documentation/design-tokens.rn.ts new file mode 100644 index 00000000..c0f996e3 --- /dev/null +++ b/documentation/design-tokens.rn.ts @@ -0,0 +1,531 @@ +/** + * Y STEM and Chess — React Native Design Tokens + * ============================================================ + * Import this file into any React Native / Expo component: + * + * import { colors, spacing, typography, radius, shadows } from './design-tokens.rn'; + * + * All values are React Native-compatible (unitless numbers for + * dimensions, hex strings for colors, named shadow objects for + * iOS/Android). No CSS units (rem, px) are used. + * + * Mirrors design-tokens.css and tailwind.config.js exactly. + * See design.md for usage rules and anti-patterns. + * ============================================================ + */ + +import { Platform, TextStyle, ViewStyle } from 'react-native'; + + +/* ============================================================ + 1. COLOR PALETTE + Source: tailwind.config.js → theme.extend.colors + ============================================================ */ + +export const colors = { + // Brand greens + primary: '#7FCC26', // Main brand green — CTAs, active states, focus rings + secondary: '#BFD99E', // Muted green — hover/inactive states, icon tints + soft: '#E5F3D2', // Light green — screen backgrounds, card tints, icon containers + + // Accent + accent: '#EAD94C', // Yellow — gamification highlights, active toolbar icons, badge borders + + // Neutrals + dark: '#1F1F1F', // Near-black — primary text, borders, btn-primary background + gray: '#5C5C5C', // Secondary text — body copy, descriptions + muted: '#8A8A8A', // Placeholder text, disabled states, metadata labels + borderLight: '#D6D6D6', // Borders, dividers, separators + light: '#F9FAF7', // Off-white — card surfaces, form backgrounds, modals + + // Semantic / Error + red: '#D64545', // Errors, destructive actions, invalid input borders + redLight: '#F5E9E9', // Error state backgrounds + + // Transparent helpers + primaryFaint: 'rgba(127, 204, 38, 0.1)', // primary/10 — subtle green tint + primaryLight: 'rgba(127, 204, 38, 0.2)', // primary/20 — success icon container + primaryMid: 'rgba(127, 204, 38, 0.3)', // primary/30 — icon tint + darkOverlay: 'rgba(31, 31, 31, 0.5)', // dark/50 — modal backdrop + accentFaint: 'rgba(234, 217, 76, 0.2)', // accent/20 — tutor section bg + white10: 'rgba(255, 255, 255, 0.1)', + white20: 'rgba(255, 255, 255, 0.2)', + white50: 'rgba(255, 255, 255, 0.5)', + white60: 'rgba(255, 255, 255, 0.6)', + white80: 'rgba(255, 255, 255, 0.8)', +} as const; + +export type ColorKey = keyof typeof colors; + + +/* ============================================================ + 2. TYPOGRAPHY + Font: Lato (load via @expo-google-fonts/lato or embed assets) + ============================================================ */ + +export const fontFamily = { + /** + * Load Lato via Expo: + * import { useFonts, Lato_400Regular, Lato_700Bold } from '@expo-google-fonts/lato'; + * + * Then use fontFamily.regular, fontFamily.bold, etc. + */ + regular: 'Lato_400Regular', + medium: 'Lato_500Medium', // Load Lato_500Medium if available, else use regular + bold: 'Lato_700Bold', + + // System fallback (use until fonts load) + system: Platform.OS === 'ios' ? 'System' : 'Roboto', +} as const; + +export const fontWeight = { + normal: '400' as TextStyle['fontWeight'], + medium: '500' as TextStyle['fontWeight'], + bold: '700' as TextStyle['fontWeight'], +} as const; + +/** + * Type scale — matches the Tailwind/web type scale. + * Values are unitless numbers (React Native interprets as dp/pt). + */ +export const fontSize = { + xs: 12, // Captions, metadata, copyright labels + sm: 14, // Labels, timestamps, small body text + base: 16, // Default body text + lg: 18, // Nav links, slightly larger body + xl: 20, // Large body, card descriptions, btn-green text + '2xl': 24, // Sub-headings, footer wordmark + '3xl': 30, // Section headings + '4xl': 36, // Hero headings +} as const; + +export const lineHeight = { + tight: 1.25, + normal: 1.5, + relaxed: 1.625, // Default for body text blocks +} as const; + +/** Convenience: multiply fontSize by lineHeight to get absolute lineHeight for RN */ +export const getLineHeight = (size: number, ratio: number = lineHeight.relaxed) => + Math.round(size * ratio); + + +/* ============================================================ + 3. SPACING SCALE + 4px base, matches Tailwind default spacing. + Values are unitless numbers (dp/pt in React Native). + ============================================================ */ + +export const spacing = { + 0: 0, + 1: 4, // space-1 + 2: 8, // space-2 + 3: 12, // space-3 + 4: 16, // space-4 + 5: 20, // space-5 + 6: 24, // space-6 + 8: 32, // space-8 + 10: 40, // space-10 + 12: 48, // space-12 + 14: 56, // space-14 + 16: 64, // space-16 + 20: 80, // space-20 + 24: 96, // space-24 +} as const; + + +/* ============================================================ + 4. BORDER RADIUS + Values are unitless numbers (dp/pt in React Native). + ============================================================ */ + +export const radius = { + sm: 2, // Rarely used + md: 6, // Small elements + lg: 8, // Dropdowns, icon containers — rounded-lg + xl: 12, // Inputs, btn-green — rounded-xl + '2xl': 16, // Modals, form cards — rounded-2xl + '3xl': 24, // Brand tier cards — rounded-3xl + full: 9999, // Full pill — btn-primary, avatars, dots +} as const; + + +/* ============================================================ + 5. SHADOWS + React Native requires separate iOS and Android shadow styles. + Use the spread operator to apply: { ...shadows.sm } + ============================================================ */ + +export const shadows = { + sm: Platform.select({ + ios: { + shadowColor: '#000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.05, + shadowRadius: 2, + }, + android: { elevation: 1 }, + }) as ViewStyle, + + md: Platform.select({ + ios: { + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.10, + shadowRadius: 4, + }, + android: { elevation: 3 }, + }) as ViewStyle, + + lg: Platform.select({ + ios: { + shadowColor: '#000', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.10, + shadowRadius: 8, + }, + android: { elevation: 6 }, + }) as ViewStyle, + + xl: Platform.select({ + ios: { + shadowColor: '#000', + shadowOffset: { width: 0, height: 8 }, + shadowOpacity: 0.10, + shadowRadius: 16, + }, + android: { elevation: 12 }, + }) as ViewStyle, + + /** + * Brand card shadows — the most visually distinctive pattern. + * Offset yellow/green shadow used on the Free and Premium tier cards. + * Note: React Native doesn't support offset shadows the same way CSS does. + * These approximate the visual with a colored, offset shadow. + */ + cardYellow: Platform.select({ + ios: { + shadowColor: 'rgb(209, 230, 28)', + shadowOffset: { width: 10, height: 10 }, + shadowOpacity: 0.9, + shadowRadius: 1, + }, + android: { elevation: 8 }, // Android elevation doesn't support colored shadows + }) as ViewStyle, + + cardGreen: Platform.select({ + ios: { + shadowColor: 'rgb(115, 179, 19)', + shadowOffset: { width: 10, height: 10 }, + shadowOpacity: 0.9, + shadowRadius: 1, + }, + android: { elevation: 8 }, + }) as ViewStyle, +} as const; + + +/* ============================================================ + 6. ANIMATION DURATIONS + Use with React Native's Animated API or react-native-reanimated. + Values in milliseconds. + ============================================================ */ + +export const duration = { + fast: 150, // Modal entry, snappy micro-interactions + base: 300, // Nav transitions, card hover equivalents + slow: 500, // Button color transitions +} as const; + + +/* ============================================================ + 7. COMPONENT STYLE PRESETS + Ready-to-use StyleSheet-compatible objects for common patterns. + Spread into your StyleSheet.create() definitions. + ============================================================ */ + +/** + * Button: Primary + * Dark pill CTA. Equivalent to .btn-primary on web. + * Usage: "Donate", "Join Now!", "Get Started!" + */ +export const btnPrimaryStyle = { + backgroundColor: colors.dark, + borderRadius: radius.full, + borderWidth: 2, + borderColor: colors.dark, + paddingVertical: spacing[3], + paddingHorizontal: spacing[8], + alignItems: 'center' as const, + justifyContent: 'center' as const, +}; + +export const btnPrimaryTextStyle: TextStyle = { + color: colors.light, + fontFamily: fontFamily.bold, + fontSize: fontSize.base, + fontWeight: fontWeight.bold, + lineHeight: getLineHeight(fontSize.base, lineHeight.relaxed), +}; + +/** + * Button: Green + * Primary brand green rounded button. Equivalent to .btn-green on web. + * Usage: "Enter", "Start Lesson", "Let's Go!" + */ +export const btnGreenStyle = { + backgroundColor: colors.primary, + borderRadius: radius.xl, + paddingVertical: spacing[3], + paddingHorizontal: spacing[8], + alignItems: 'center' as const, + justifyContent: 'center' as const, + ...shadows.md, +}; + +export const btnGreenTextStyle: TextStyle = { + color: colors.light, + fontFamily: fontFamily.bold, + fontSize: fontSize.xl, + fontWeight: fontWeight.bold, +}; + +/** + * Input: Default + * Rounded input with brand focus/error states. + */ +export const inputBaseStyle = { + width: '100%' as const, + borderRadius: radius.xl, + borderWidth: 2, + borderColor: colors.borderLight, + paddingVertical: spacing[3], + paddingHorizontal: spacing[4], + backgroundColor: '#ffffff', + color: colors.dark, + fontSize: fontSize.sm, + fontFamily: fontFamily.regular, +}; + +export const inputFocusStyle = { + borderColor: colors.primary, +}; + +export const inputErrorStyle = { + borderColor: colors.red, +}; + +/** + * Card: Content (subtle) + * Equivalent to .card on web. Activity entries, list items. + */ +export const cardStyle = { + backgroundColor: colors.light, + borderRadius: radius.lg, + borderWidth: 1, + borderColor: colors.borderLight, + padding: spacing[4], + ...shadows.sm, +}; + +/** + * Card: Brand Green (Free tier) + * Equivalent to .card-brand-green on web. + */ +export const cardBrandGreenStyle = { + backgroundColor: colors.primary, + borderRadius: radius['3xl'], + padding: spacing[8], + alignItems: 'center' as const, + ...shadows.cardYellow, +}; + +/** + * Card: Brand Light (Premium tier) + * Equivalent to .card-brand-light on web. + */ +export const cardBrandLightStyle = { + backgroundColor: colors.light, + borderRadius: radius['3xl'], + padding: spacing[8], + alignItems: 'center' as const, + ...shadows.cardGreen, +}; + +/** + * Card: Form / Auth panel + * Equivalent to .card-form on web. Login, signup. + */ +export const cardFormStyle = { + backgroundColor: colors.light, + borderRadius: radius['2xl'], + borderWidth: 2, + borderColor: colors.dark, + padding: spacing[8], + ...shadows.md, +}; + +/** + * Modal overlay + * Full-screen dark backdrop. Equivalent to .modal-overlay on web. + */ +export const modalOverlayStyle = { + flex: 1, + backgroundColor: colors.darkOverlay, + alignItems: 'center' as const, + justifyContent: 'center' as const, + padding: spacing[4], +}; + +/** + * Modal content panel + * Equivalent to .modal-content on web. + */ +export const modalContentStyle = { + backgroundColor: colors.light, + width: '100%' as const, + maxWidth: 384, + borderRadius: radius['2xl'], + padding: spacing[8], + alignItems: 'center' as const, + gap: spacing[5], + ...shadows.xl, +}; + +/** + * Avatar / Profile picture circle + */ +export const avatarStyle = { + width: 56, + height: 56, + borderRadius: radius.full, + backgroundColor: colors.light, + borderWidth: 2, + borderColor: colors.primary, + alignItems: 'center' as const, + justifyContent: 'center' as const, + ...shadows.sm, +}; + +/** + * Nav link text + * Equivalent to .nav-link on web. + */ +export const navLinkTextStyle: TextStyle = { + fontSize: fontSize.lg, + fontFamily: fontFamily.medium, + fontWeight: fontWeight.medium, + color: colors.dark, +}; + +/** + * Section heading + * Equivalent to .text-section-heading on web. + */ +export const sectionHeadingStyle: TextStyle = { + fontSize: fontSize['3xl'], + fontFamily: fontFamily.bold, + fontWeight: fontWeight.bold, + color: colors.dark, + textAlign: 'center', +}; + +/** + * Hero heading + * Equivalent to .text-hero on web. + */ +export const heroHeadingStyle: TextStyle = { + fontSize: fontSize['3xl'], + fontFamily: fontFamily.bold, + fontWeight: fontWeight.bold, + color: colors.dark, + lineHeight: getLineHeight(fontSize['3xl'], lineHeight.relaxed), +}; + +/** + * Body text — primary + */ +export const bodyTextStyle: TextStyle = { + fontSize: fontSize.base, + fontFamily: fontFamily.regular, + color: colors.dark, + lineHeight: getLineHeight(fontSize.base, lineHeight.relaxed), +}; + +/** + * Body text — secondary / descriptive + */ +export const bodySecondaryTextStyle: TextStyle = { + fontSize: fontSize.base, + fontFamily: fontFamily.regular, + color: colors.gray, + lineHeight: getLineHeight(fontSize.base, lineHeight.relaxed), +}; + +/** + * Label / metadata + * Equivalent to .text-label on web. + */ +export const labelTextStyle: TextStyle = { + fontSize: fontSize.xs, + fontFamily: fontFamily.bold, + fontWeight: fontWeight.bold, + color: colors.muted, + textTransform: 'uppercase', + letterSpacing: 1.2, +}; + +/** + * Error text + */ +export const errorTextStyle: TextStyle = { + fontSize: fontSize.sm, + fontFamily: fontFamily.bold, + fontWeight: fontWeight.bold, + color: colors.red, +}; + +/** + * Timeline dot (activity feed) + */ +export const timelineDotStyle = { + width: 12, + height: 12, + borderRadius: radius.full, + backgroundColor: colors.primary, + borderWidth: 2, + borderColor: colors.light, + ...shadows.sm, +}; + + +/* ============================================================ + 8. SCREEN LAYOUT CONSTANTS + Common layout values for screen-level containers. + ============================================================ */ + +export const layout = { + screenPaddingH: spacing[6], // Horizontal padding for most screens (24px) + screenPaddingV: spacing[8], // Vertical padding for most screens (32px) + sectionSpacing: spacing[12], // Space between major sections (48px) + cardGap: spacing[4], // Gap between cards in a list (16px) + formFieldGap: spacing[6], // Gap between form fields (24px) + labelFieldGap: spacing[2], // Gap between label and input (8px) + maxFormWidth: 384, // Max width for auth/form screens (max-w-sm equivalent) + navBarHeight: 96, // NavBar height — h-24 equivalent +} as const; + + +/* ============================================================ + 9. CHESS-SPECIFIC TOKENS + Values used across the chess board UI components. + ============================================================ */ + +export const chess = { + boardBg: '#f5f9f0', + boardDotColor: 'rgba(127, 204, 38, 0.28)', + boardDotSize: 22, // Background dot grid spacing + hatchColor: 'rgba(127, 204, 38, 0.07)', + highlightSquare: 'rgba(127, 204, 38, 0.4)', + lastMoveDark: '#2D6A4F', // Dark move cell background +} as const; diff --git a/documentation/design.md b/documentation/design.md new file mode 100644 index 00000000..9978889c --- /dev/null +++ b/documentation/design.md @@ -0,0 +1,583 @@ +# Y STEM and Chess — design.md + +> **For AI coding agents:** Load this file alongside `design-tokens.css` before generating any UI for the Y STEM and Chess platform. This document is the authoritative source of truth for visual language, component patterns, copywriting, and layout decisions. Do not invent design choices — use only what is documented here. + +--- + +## 1. Brand Identity + +**Organization:** Y STEM and Chess Inc. — a nonprofit in Boise, Idaho. +**Mission:** Empower socially and economically underserved children to pursue STEM careers through chess, math, computer science, and mentoring. +**Platform:** An educational web app where students learn, play chess, take lessons, solve puzzles, and track progress with a mentor. + +### Tone +- **Encouraging and accessible** — students are often young and underserved; the platform should feel safe and welcoming +- **Warm but professional** — not childish, not corporate +- **Action-oriented** — verbs drive CTAs: Play, Learn, Empower, Join, Get Started, Donate +- **Inclusive** — "Everyone is included. Everyone is welcome." is a core brand phrase + +### Three Core Words +`Play` · `Learn` · `Empower` + +These appear in the footer and should be used as a lens when naming features or writing headings. + +--- + +## 2. Color System + +All colors are defined as Tailwind tokens in `tailwind.config.js`. **Never use raw hex values in JSX or TSX — always use Tailwind tokens or CSS variables from `design-tokens.css`.** + +### Palette + +| Token | Hex | Usage | +|---|---|---| +| `primary` | `#7FCC26` | Brand green. CTAs, active states, accents, toolbar backgrounds, progress indicators, timeline dots, focus rings | +| `secondary` | `#BFD99E` | Muted green. Hover states on SVG icons, inactive toolbar icons, supporting accent | +| `soft` | `#E5F3D2` | Light green. Page background (with chess piece SVG pattern), card backgrounds, icon containers, scrollbar track | +| `accent` | `#EAD94C` | Yellow. Active toolbar icon color, badge highlights, book card borders | +| `dark` | `#1F1F1F` | Near-black. Primary text, navbar border, footer border, card borders, button bg for `btn-primary` | +| `gray` | `#5C5C5C` | Secondary text. Body copy, nav link color (non-hover), dropdown items, timestamps, description text | +| `muted` | `#8A8A8A` | Placeholder text, disabled states, uppercase labels in footer contact section | +| `borderLight` | `#D6D6D6` | Borders, dividers, tab dividers, card borders in low-emphasis contexts | +| `light` | `#F9FAF7` | Off-white. Navbar background, footer background, form backgrounds, modal backgrounds, card surfaces, dropdown surfaces | +| `red` | `#D64545` | Errors, destructive actions, validation failure borders | +| `redLight` | `#F5E9E9` | Error state backgrounds (e.g., error modal icon container) | + +### Color Rules + +**Do:** +- Use `primary` for interactive elements that signal "do this" (buttons, links on hover, focus rings) +- Use `dark` on `light` for primary text — maximum contrast +- Use `gray` on `light` for secondary/descriptive text +- Use `soft` as the default page background (it includes a chess-piece SVG pattern) +- Use `accent` only for highlights and gamification elements — not for primary actions +- Use `primary/20` or `primary/30` (Tailwind opacity modifier) for subtle icon container tints + +**Don't:** +- Use hardcoded hex values anywhere in JSX/TSX +- Use `primary` as a text color on `soft` backgrounds (insufficient contrast at small sizes) +- Use `accent` for error or destructive states — that's `red` / `redLight` +- Use `secondary` as a background for large areas — it's for hover states and icon tints +- Mix `gray` and `muted` arbitrarily — `gray` is for body content, `muted` is for metadata/labels + +--- + +## 3. Typography + +### Font + +**Family:** `Lato` +**Fallbacks:** `system-ui, -apple-system, BlinkMacSystemFont, sans-serif` +**Loaded from:** Google Fonts (weights 400, 500, 700) + +```html + +``` + +The body always receives `font-family: Lato, ...` via the Tailwind `font-sans` extension. Do not apply a different font family to any element. + +### Type Scale (observed in production) + +| Role | Tailwind | Size | Weight | Color | Notes | +|---|---|---|---|---|---| +| Hero H1 | `text-3xl md:text-4xl font-bold` | 30–36px | 700 | `text-dark` | Left-aligned, `leading-relaxed` | +| Section H2 | `text-3xl md:text-4xl font-bold` | 30–36px | 700 | `text-dark` | Often centered | +| Card H3 | `text-xl md:text-2xl font-bold` | 20–24px | 700 | `text-dark` | | +| Nav links | `text-lg font-medium` | 18px | 500 | `text-dark` | Hover: `text-primary` | +| Body large | `text-xl md:text-2xl` | 20–24px | 400 | `text-gray` | Used in hero subtext | +| Body default | `text-base` | 16px | 400 | `text-dark` or `text-gray` | | +| Body small | `text-sm` | 14px | 400 | `text-gray` | Descriptions, timestamps | +| Caption / Label | `text-xs font-bold uppercase tracking-widest` | 12px | 700 | `text-muted` | Footer copyright, metadata labels | +| Dropdown section header | `text-base font-bold uppercase tracking-wide` | 16px | 700 | `text-dark` | | + +### Typography Rules + +- **Line height:** Use `leading-relaxed` (1.625) for body text blocks. Use `leading-normal` for default. Use `leading-tight` only for display/hero headings where line-height would cause too much space. +- **Bold usage:** `font-bold` (700) for headings and labels. `font-semibold` (600) for button text. `font-medium` (500) for nav links and interactive text. `font-normal` (400) for body copy. +- **Uppercase:** Only use for labels, metadata, section sub-labels, and the footer tagline. Never uppercase primary headings or CTA text. +- **Text colors on primary backgrounds:** Use `text-light` (`#F9FAF7`) for text placed on `bg-primary` green backgrounds. + +--- + +## 4. Layout & Spacing + +### Container Widths + +| Context | Tailwind | Notes | +|---|---|---| +| Standard page sections | `max-w-7xl mx-auto px-6 md:px-8` | Hero, books, sponsors | +| Full-bleed dashboard | `max-w-screen-2xl mx-auto px-6` | Student profile, mentor dashboard | +| Narrow forms | `max-w-sm` | Login, signup — centered with `flex items-center justify-center` | +| Modal content | `max-w-sm` | Always centered in a full-screen overlay | +| Dropdown menus | `w-64` (About Us), `w-48` (Profile) | Absolute positioned | + +### Page Structure + +Every page follows this shell: +``` + ← sticky top-0 z-50, bg-light, border-b-2 border-dark, h-24 +
← route-specific content +
← content regions, vertically spaced with py-10 to py-16 +
+