From dfbf24ae1de4e07e1532070a2cadfdb3424472e5 Mon Sep 17 00:00:00 2001 From: William <248649537+stupidgiraffe@users.noreply.github.com> Date: Fri, 2 Oct 2026 04:06:40 -0400 Subject: [PATCH 1/7] Add designer card themes --- src/lib/types.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lib/types.ts b/src/lib/types.ts index a6f65ba..322818c 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -127,6 +127,14 @@ export type CardTheme = | 'playful-pop' | 'teacher-pro' | 'calm-study' + | 'dreamy-classroom' + | 'storybook' + | 'notebook-doodle' + | 'retro-schoolhouse' + | 'botanical-study' + | 'candy-pop' + | 'space-explorer' + | 'modern-editorial' export interface PrintSettings { cardsPerPage: CardsPerPage From 10fbf3021989b2d13aac01b87c237164ca2c7c49 Mon Sep 17 00:00:00 2001 From: William <248649537+stupidgiraffe@users.noreply.github.com> Date: Fri, 2 Oct 2026 04:06:43 -0400 Subject: [PATCH 2/7] Add designer card themes --- src/index.css | 257 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) diff --git a/src/index.css b/src/index.css index 767b23c..bf3af80 100644 --- a/src/index.css +++ b/src/index.css @@ -109,6 +109,263 @@ --radius-full: 9999px; } + + +.flash-card-surface { + --card-main: #1e293b; + --card-accent: #0ea5e9; + --card-theme-gold: #c6a252; + --card-theme-soft: #fff7ed; + --card-theme-tape: #f0d79b; + --card-theme-pink: #f7a8c8; + --card-theme-cyan: #8fdde7; + --card-theme-yellow: #f7d56a; + --card-theme-planet: #a78bfa; + isolation: isolate; + background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%); + } + + .flash-card-surface[data-card-theme='minimal'] { + background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%); + } + + .flash-card-surface[data-card-theme='classroom-cute'] { + background: linear-gradient(135deg, #fffbeb 0%, #fff7ed 100%); + } + + .flash-card-surface[data-card-theme='bold-vocabulary'] { + background: linear-gradient(135deg, #eff6ff 0%, #faf5ff 100%); + } + + .flash-card-surface[data-card-theme='picture-focus'] { + background: linear-gradient(135deg, #f9fafb 0%, #f1f5f9 100%); + } + + .flash-card-surface[data-card-theme='ink-saver'] { + background: #ffffff; + } + + .flash-card-surface[data-card-theme='quiz-card'] { + background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%); + } + + .flash-card-surface[data-card-theme='playful-pop'] { + background: + radial-gradient(circle at 15% 15%, rgba(244, 114, 182, 0.12), transparent 28%), + radial-gradient(circle at 85% 82%, rgba(34, 211, 238, 0.12), transparent 30%), + #ffffff; + } + + .flash-card-surface[data-card-theme='teacher-pro'] { + background: linear-gradient(135deg, #ffffff 0%, #f8fafc 54%, #eff6ff 100%); + } + + .flash-card-surface[data-card-theme='calm-study'] { + background: linear-gradient(135deg, #ecfdf5 0%, #f0fdfa 100%); + } + + .flash-card-surface[data-card-theme='dreamy-classroom'] { + --card-theme-gold: #c6a252; + --card-theme-soft: #fff7ed; + background: + radial-gradient(circle at 20% 10%, rgba(199, 174, 230, 0.22), transparent 30%), + radial-gradient(circle at 83% 83%, rgba(244, 199, 221, 0.18), transparent 32%), + linear-gradient(145deg, #fffdf8 0%, #fffaf2 48%, #fbf7ff 100%); + } + + .flash-card-surface[data-card-theme='storybook'] { + --card-theme-soft: #fff1e8; + background: + radial-gradient(circle at 15% 18%, rgba(240, 176, 153, 0.18), transparent 28%), + radial-gradient(circle at 87% 78%, rgba(172, 199, 159, 0.17), transparent 34%), + linear-gradient(145deg, #fffdf7 0%, #fff8f1 58%, #f7fbf2 100%); + } + + .flash-card-surface[data-card-theme='notebook-doodle'] { + --card-theme-tape: #f2d28c; + background-color: #fffdf7; + background-image: + linear-gradient(90deg, transparent 0 12%, rgba(231, 111, 81, 0.16) 12% 12.4%, transparent 12.4% 100%), + repeating-linear-gradient(0deg, transparent 0 20px, rgba(81, 126, 164, 0.13) 20px 21px); + } + + .flash-card-surface[data-card-theme='retro-schoolhouse'] { + background: + repeating-linear-gradient(0deg, rgba(108, 75, 48, 0.025) 0 1px, transparent 1px 4px), + linear-gradient(145deg, #fffaf0 0%, #f8efd9 100%); + } + + .flash-card-surface[data-card-theme='botanical-study'] { + background: + radial-gradient(circle at 18% 82%, rgba(123, 155, 121, 0.13), transparent 32%), + radial-gradient(circle at 84% 12%, rgba(84, 116, 94, 0.12), transparent 30%), + linear-gradient(145deg, #fbfcf7 0%, #f3f7ef 100%); + } + + .flash-card-surface[data-card-theme='candy-pop'] { + --card-theme-pink: #f7a8c8; + --card-theme-cyan: #8fdde7; + --card-theme-yellow: #f7d56a; + background: + radial-gradient(circle at 10% 12%, rgba(247, 168, 200, 0.28), transparent 24%), + radial-gradient(circle at 88% 20%, rgba(143, 221, 231, 0.24), transparent 25%), + radial-gradient(circle at 78% 88%, rgba(247, 213, 106, 0.22), transparent 28%), + linear-gradient(145deg, #fffaff 0%, #fffdf8 100%); + } + + .flash-card-surface[data-card-theme='space-explorer'] { + --card-theme-planet: #a78bfa; + background: + radial-gradient(circle at 78% 18%, rgba(99, 102, 241, 0.44), transparent 26%), + radial-gradient(circle at 18% 84%, rgba(8, 145, 178, 0.24), transparent 32%), + linear-gradient(145deg, #111936 0%, #18214a 45%, #0f172a 100%); + } + + .flash-card-surface[data-card-theme='modern-editorial'] { + background: + linear-gradient(118deg, rgba(232, 93, 63, 0.08) 0 18%, transparent 18% 100%), + linear-gradient(145deg, #ffffff 0%, #f7f7f5 100%); + } + + .flash-card-surface[data-card-theme='dreamy-classroom'] .flash-card-content, + .flash-card-surface[data-card-theme='storybook'] .flash-card-content, + .flash-card-surface[data-card-theme='retro-schoolhouse'] .flash-card-content, + .flash-card-surface[data-card-theme='botanical-study'] .flash-card-content { + padding: 1.75rem; + } + + .flash-card-surface[data-card-theme='notebook-doodle'] .flash-card-content { + padding-left: 2rem; + } + + .flash-card-surface[data-card-theme='modern-editorial'] .flash-card-content { + padding-left: 2.25rem; + padding-right: 1.75rem; + } + + .flash-card-surface[data-card-theme='dreamy-classroom'] .flash-card-primary, + .flash-card-surface[data-card-theme='storybook'] .flash-card-primary { + letter-spacing: 0.015em; + } + + .flash-card-surface[data-card-theme='retro-schoolhouse'] .flash-card-primary { + letter-spacing: 0.025em; + text-transform: none; + } + + .flash-card-surface[data-card-theme='modern-editorial'] .flash-card-primary { + letter-spacing: -0.025em; + } + + .flash-card-surface[data-card-theme='space-explorer'] .flash-card-secondary { + color: var(--card-accent) !important; + } + + .flash-card-surface[data-card-theme='dreamy-classroom'] .flash-card-image-frame { + border: 1px solid rgba(198, 162, 82, 0.7); + border-radius: 14px; + background: rgba(255, 255, 255, 0.72); + padding: 4px; + } + + .flash-card-surface[data-card-theme='storybook'] .flash-card-image-frame { + border: 2px solid var(--card-accent); + border-radius: 18px 18px 12px 12px; + background: #fffdf7; + padding: 4px; + } + + .flash-card-surface[data-card-theme='notebook-doodle'] .flash-card-image-frame { + border: 2px solid var(--card-main); + border-radius: 4px; + background: #ffffff; + padding: 5px; + transform: rotate(-0.7deg); + } + + .flash-card-surface[data-card-theme='retro-schoolhouse'] .flash-card-image-frame { + border: 2px solid var(--card-main); + border-radius: 2px; + background: #fffaf0; + padding: 4px; + } + + .flash-card-surface[data-card-theme='botanical-study'] .flash-card-image-frame { + border: 1px solid var(--card-accent); + border-radius: 14px; + background: #ffffff; + padding: 5px; + } + + .flash-card-surface[data-card-theme='candy-pop'] .flash-card-image-frame { + border: 3px solid rgba(255, 255, 255, 0.86); + border-radius: 20px; + background: #ffffff; + padding: 4px; + box-shadow: 0 5px 0 rgba(91, 58, 122, 0.08); + } + + .flash-card-surface[data-card-theme='space-explorer'] .flash-card-image-frame { + border: 1px solid var(--card-accent); + border-radius: 14px; + background: rgba(15, 23, 42, 0.78); + padding: 4px; + } + + .flash-card-surface[data-card-theme='modern-editorial'] .flash-card-image-frame { + border: 0; + border-radius: 2px; + background: #ffffff; + padding: 0; + } + + .flash-card-surface[data-card-theme='dreamy-classroom'] .flash-card-set-title, + .flash-card-surface[data-card-theme='storybook'] .flash-card-set-title, + .flash-card-surface[data-card-theme='retro-schoolhouse'] .flash-card-set-title { + opacity: 0.92; + border-radius: 999px; + background: var(--card-accent); + color: #ffffff !important; + padding: 0.18rem 0.5rem; + } + + .flash-card-surface[data-card-theme='notebook-doodle'] .flash-card-set-title { + opacity: 0.86; + background: #fff4cf; + border: 1px solid var(--card-accent); + color: var(--card-main) !important; + padding: 0.15rem 0.45rem; + transform: rotate(1deg); + } + + .flash-card-surface[data-card-theme='space-explorer'] .flash-card-set-title { + opacity: 0.9; + color: var(--card-main) !important; + border: 1px solid var(--card-accent); + border-radius: 999px; + padding: 0.16rem 0.45rem; + background: rgba(15, 23, 42, 0.55); + } + + .flash-card-surface[data-card-theme='modern-editorial'] .flash-card-set-title { + opacity: 1; + color: var(--card-accent) !important; + font-weight: 800; + letter-spacing: 0.06em; + text-transform: uppercase; + } + + .flash-card-surface[data-card-theme='dreamy-classroom'] .flash-card-footer, + .flash-card-surface[data-card-theme='storybook'] .flash-card-footer, + .flash-card-surface[data-card-theme='botanical-study'] .flash-card-footer { + margin-inline: 1.5rem; + } + +.theme-preview-card { + min-height: 7.5rem; +} + + /* * Keep the print portal laid out off-screen instead of display:none. * PlacedImage measures its frame with ResizeObserver; display:none gives it From 4bda46b7780f6162aad881df2d3830518094baaf Mon Sep 17 00:00:00 2001 From: William <248649537+stupidgiraffe@users.noreply.github.com> Date: Fri, 2 Oct 2026 04:06:46 -0400 Subject: [PATCH 3/7] Add designer card themes --- src/components/FlashCardDisplay.tsx | 52 ++++++++++++----------------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/src/components/FlashCardDisplay.tsx b/src/components/FlashCardDisplay.tsx index 0dd62d4..8de067d 100644 --- a/src/components/FlashCardDisplay.tsx +++ b/src/components/FlashCardDisplay.tsx @@ -1,8 +1,11 @@ +import type { CSSProperties } from 'react' import type { FlashCard, PrintSettings } from '@/lib/types' import { PlacedImage } from '@/components/PlacedImage' import { getCardSideImage } from '@/lib/image-placement' import { calculateFontSize } from '@/lib/print-utils' import { cn } from '@/lib/utils' +import { CardThemeDecorations } from '@/components/CardThemeDecorations' +import { isDesignerCardTheme } from '@/lib/card-themes' interface FlashCardDisplayProps { card: FlashCard | null @@ -63,7 +66,7 @@ export function FlashCardDisplay({ ? settings.fontSize : calculateFontSize(settings.fontSize, settings.cardsPerPage, textLength, !!imageUrl && hasText) const secondaryFontSize = fontSize * 0.72 - const themeClasses = getThemeClasses(settings.theme) + const isDesignerTheme = isDesignerCardTheme(settings.theme) const borderClass = settings.showBorder ? 'border-2' : 'border-0' const roundedClass = settings.showRoundedCorners ? 'rounded-xl' : 'rounded-none' @@ -80,20 +83,24 @@ export function FlashCardDisplay({ return (
Print-safe CSS and vector artwork. No external image packs or licensing dependencies.
+Everything FlashForge already had remains available.
+Choosing a theme applies its starting palette but does not change paper size, cards per page, duplex alignment, offsets, or your card content. You can customize colors and typography below.
+Fine-tune colors, typography, borders, alignment, and image balance. Any change shows the style as Customized.
From bae52cff0a1f75d42ad86dad17b693628d57effa Mon Sep 17 00:00:00 2001 From: William <248649537+stupidgiraffe@users.noreply.github.com> Date: Fri, 2 Oct 2026 04:06:51 -0400 Subject: [PATCH 5/7] Add designer card themes --- src/lib/card-themes.ts | 121 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 src/lib/card-themes.ts diff --git a/src/lib/card-themes.ts b/src/lib/card-themes.ts new file mode 100644 index 0000000..d245a99 --- /dev/null +++ b/src/lib/card-themes.ts @@ -0,0 +1,121 @@ +import type { CardTheme, PrintSettings } from './types' + +export type CardThemeFamily = 'classic' | 'designer' + +export interface CardThemeDefinition { + id: CardTheme + name: string + description: string + family: CardThemeFamily + mainColor: string + accentColor: string + previewLabel: string +} + +export const CLASSIC_CARD_THEMES: CardThemeDefinition[] = [ + { id: 'teacher-pro', name: 'Teacher Pro', description: 'Clean blue editorial styling', family: 'classic', mainColor: '#183153', accentColor: '#0891b2', previewLabel: 'ABC' }, + { id: 'minimal', name: 'Minimal', description: 'Quiet modern neutral', family: 'classic', mainColor: '#1e293b', accentColor: '#64748b', previewLabel: 'Aa' }, + { id: 'classroom-cute', name: 'Classroom Cute', description: 'Warm and friendly', family: 'classic', mainColor: '#7c2d12', accentColor: '#0f766e', previewLabel: 'Hi!' }, + { id: 'bold-vocabulary', name: 'Bold Vocabulary', description: 'High-energy contrast', family: 'classic', mainColor: '#111827', accentColor: '#dc2626', previewLabel: 'WORD' }, + { id: 'picture-focus', name: 'Picture Focus', description: 'Let visuals lead', family: 'classic', mainColor: '#111827', accentColor: '#38bdf8', previewLabel: 'Photo' }, + { id: 'quiz-card', name: 'Quiz Card', description: 'Structured assessment look', family: 'classic', mainColor: '#172554', accentColor: '#ca8a04', previewLabel: 'Q?' }, + { id: 'playful-pop', name: 'Playful Pop', description: 'Bright kid-friendly palette', family: 'classic', mainColor: '#7c3aed', accentColor: '#ec4899', previewLabel: 'POP' }, + { id: 'calm-study', name: 'Calm Study', description: 'Soft green focus mode', family: 'classic', mainColor: '#14532d', accentColor: '#0d9488', previewLabel: 'Study' }, + { id: 'ink-saver', name: 'Ink Saver', description: 'Economical for classroom printing', family: 'classic', mainColor: '#111111', accentColor: '#555555', previewLabel: 'Aa' }, +] + +export const DESIGNER_CARD_THEMES: CardThemeDefinition[] = [ + { + id: 'dreamy-classroom', + name: 'Dreamy Classroom', + description: 'Ornamental gold frame, lavender accents, clouds, sparkles, and ribbon details', + family: 'designer', + mainColor: '#342a4f', + accentColor: '#8b6bbd', + previewLabel: 'DREAM', + }, + { + id: 'storybook', + name: 'Storybook', + description: 'Soft illustrated framing with warm paper, scallops, stars, and gentle curves', + family: 'designer', + mainColor: '#4d3d43', + accentColor: '#d98273', + previewLabel: 'Story', + }, + { + id: 'notebook-doodle', + name: 'Notebook Doodle', + description: 'Ruled-paper energy with tape, hand-drawn stars, arrows, and classroom doodles', + family: 'designer', + mainColor: '#1f3b5b', + accentColor: '#e76f51', + previewLabel: 'NOTE', + }, + { + id: 'retro-schoolhouse', + name: 'Retro Schoolhouse', + description: 'Warm vintage textbook styling with cream stock, burgundy, mustard, and badges', + family: 'designer', + mainColor: '#6b2737', + accentColor: '#d89b31', + previewLabel: 'LESSON', + }, + { + id: 'botanical-study', + name: 'Botanical Study', + description: 'Elegant sage paper with fine botanical corners and calm study-room typography', + family: 'designer', + mainColor: '#29483a', + accentColor: '#7b9b79', + previewLabel: 'Study', + }, + { + id: 'candy-pop', + name: 'Candy Pop', + description: 'Polished pastel bubbles, stickers, and playful shapes without looking chaotic', + family: 'designer', + mainColor: '#5b3a7a', + accentColor: '#ef6fa8', + previewLabel: 'YAY!', + }, + { + id: 'space-explorer', + name: 'Space Explorer', + description: 'Deep indigo card stock with constellations, planets, and bright classroom-safe contrast', + family: 'designer', + mainColor: '#eef2ff', + accentColor: '#67e8f9', + previewLabel: 'SPACE', + }, + { + id: 'modern-editorial', + name: 'Modern Editorial', + description: 'Strong typography, offset geometry, and premium magazine-style composition', + family: 'designer', + mainColor: '#1f2937', + accentColor: '#e85d3f', + previewLabel: 'TYPE', + }, +] + +export const CARD_THEMES: CardThemeDefinition[] = [...DESIGNER_CARD_THEMES, ...CLASSIC_CARD_THEMES] + +const THEME_BY_ID = new Map