This file tracks significant changes, design decisions, and implementation patterns across development sessions for the jorgecalderon.codes Storybook project.
- Created:
ContentElementscomponent indesign-specs/to showcase how all standard HTML content elements render with the project's design system - Sections Displayed:
- Headings (H1–H6), each followed by a descriptive paragraph
- Paragraph & inline elements (bold, italic, bold-italic, inline code)
- Links (inline within text + standalone in a list)
- Unordered list (with nested sub-items)
- Ordered list (with nested numbered sub-items)
- Blockquote (two examples, one with footer/attribution)
- Code block (TypeScript sample in JetBrains Mono)
- Variants:
light(default) anddark(charcoal background, lighter text colors) - Styling:
- Section labels: uppercase, letterspaced, Azure Bolt underline
- Links: Azure Bolt with underline, Deep Azure on hover (dark variant uses Sky Blue)
- Blockquotes: Azure Bolt left border, subtle background tint, italic text
- Code blocks: Obsidian background, Quicksilver text, JetBrains Mono font, Azure Bolt left border
- Inline code: Pewter background tint, Deep Azure text (dark variant uses Sky Blue)
- Dark variant: Charcoal background on the component wrapper with padding and border-radius
- Stories: Light and Dark, with Dark using charcoal (
#2D2D2D) page background - Files Created:
src/stories/design-specs/ContentElements.tsxsrc/stories/design-specs/ContentElements.stories.tsxsrc/stories/assets/css/_content-elements.scss
- Files Modified:
src/stories/assets/css/main.scss— Added@use 'content-elements'
- Issue: Card
iconprop was typed asReactNode, which caused Storybook autodocs to crash with "Objects are not valid as a React child" error when rendering the controls table - Fix: Changed
iconprop fromReactNodetostring(icon class name)- Component now renders
<i className={icon} />internally - Removed
ReactNodeimport (no longer needed)
- Component now renders
- Stories: Added
iconas aselectdropdown in argTypes with Font Awesome icon options (see Icon Library Migration below) - Files Modified:
src/stories/components/Card.tsx— Changed prop type, renders<i>element internallysrc/stories/components/Card.stories.tsx— Added icon select dropdown to argTypes
- Reason: Bootstrap Icons lacked brand icons (e.g. Drupal) needed for the Skills plain cards
- Package Swap:
- Removed
bootstrap-icons(^1.13.1) - Installed
@fortawesome/fontawesome-free(^7.1.0)
- Removed
- Global Import:
.storybook/preview.tschanged frombootstrap-icons/font/bootstrap-icons.cssto@fortawesome/fontawesome-free/css/all.min.css - Icon Prop Pattern Change:
- Previously: icon prop took a short name (e.g.
'code-slash'), Card rendered<i className="bi bi-{icon}" /> - Now: icon prop takes the full Font Awesome class string (e.g.
'fa-solid fa-code','fa-brands fa-drupal'), Card renders<i className={icon} /> - This supports mixing icon styles (
fa-solid,fa-brands,fa-regular) in a single prop
- Previously: icon prop took a short name (e.g.
- Icon Mappings Applied:
code-slash→fa-solid fa-codeglobe→fa-solid fa-globerocket→fa-solid fa-rocketcloud→fa-solid fa-cloudpalette→fa-solid fa-palettedrupal→fa-brands fa-drupal(new — not available in Bootstrap Icons)list(hamburger) →fa-solid fa-barsx-lg(close) →fa-solid fa-xmarkperson-circle→fa-solid fa-circle-usergithub→fa-brands fa-githubunity→fa-brands fa-unity
- Files Modified:
package.json— Swapped dependency.storybook/preview.ts— Swapped CSS importsrc/stories/components/Card.tsx— Changed icon rendering to use full class string, updated JSDocsrc/stories/components/Card.stories.tsx— Updated all icon options and story icon valuessrc/stories/layout/Header.tsx— Updated hamburger (fa-bars) and close (fa-xmark) iconssrc/stories/modules/AboutMe.tsx— Updated placeholder icon (fa-circle-user)src/stories/modules/Skills.tsx— Updated JSDoc commentsrc/stories/modules/Skills.stories.tsx— Updated sample skill icon valuessrc/stories/pages/Home.tsx— Updated skill icon valuesCLAUDE.md— Updated tech stack and global imports references
- Verified:
npm run build-storybookcompleted successfully after all changes
- Purpose: Minimal icon + text card for showcasing skills on the homepage, displayed below the Jumbotron
- Design: Horizontal layout (icon left, content right) on transparent background — no border, no border-radius, no shadow, no hover effects
- Implementation:
- Added
'plain'toCardVarianttype:'default' | 'charcoal' | 'plain' - Created
.card--plainmodifier class incard.scss flex-direction: rowwith1remgap for horizontal icon + content layout- Icon:
1.25remsize,$pewtercolor - Title:
1.1rem,font-weight: 600,$quicksilvercolor - Description:
0.9rem,$pewtercolor,line-height: 1.55 - Body padding reset to
0(icon provides the left spacing) - All hover effects disabled (
transform: none,box-shadow: none,animation: none)
- Added
- Stories Added:
PlainTextOnly— Icon + title + descriptionPlainTitleOnly— Icon + title onlyPlainGrid(Plain — Grid 2×2) — Four skills in a 2-column Bootstrap grid
- Story Parameters: All Plain stories display on Obsidian background (
#1A1A1A) - Initial Approach: Started as a standalone
SkillCardcomponent with link/badge props, then simplified per user feedback (no links needed — purely a skill showcase), then merged into Card as a variant - Files Deleted:
src/stories/components/SkillCard.tsx— Standalone component removedsrc/stories/components/SkillCard.stories.tsx— Standalone stories removedsrc/stories/assets/css/_skill-card.scss— Standalone partial removed
- Files Modified:
src/stories/components/Card.tsx— Added'plain'toCardVarianttypesrc/stories/assets/css/card.scss— Added.card--plainmodifier stylessrc/stories/components/Card.stories.tsx— Added'plain'to variant argTypes, added 3 Plain variant storiessrc/stories/assets/css/main.scss— Removed@use 'skill-card'
- Verified:
npm run build-storybookcompleted successfully after Plain variant changes
- Purpose: 2-column grid of plain cards showcasing major skills, displayed below the Jumbotron on the Home page
- Component:
Skillsinmodules/— acceptsheading(React.ReactNode) andskillsarray - Heading: Two-tone
<h2>matching Jumbotron color pattern- "My" uses
$deep-azure(.skills__heading-accent) — same as "Hi, I'm Jorge" - "Skills." uses
$frost(.skills__heading-light) — same as "I build with Drupal." - Manrope font,
1.75rem,fw-bold
- "My" uses
- Divider: Reuses
Dividercomponent withdivider--blue-100and Bootstrapw-25class - Grid: Bootstrap
row g-0withcol-12 col-md-6for each skill card (variant="plain") - Alignment Fix: Removed
padding-leftfrom.skills__headingand changed.card--plainpadding from1.5rem(all sides) to1.25rem 0(vertical only) so Skills content aligns flush with the Jumbotron container edge - Stories:
Default(with two-tone heading) andNoHeadingvariant; file uses.tsxfor JSX heading - Files Created:
src/stories/modules/Skills.tsx— Module componentsrc/stories/modules/Skills.stories.tsx— Stories with heading and no-heading variantssrc/stories/assets/css/_skills.scss— Section and heading styles
- Files Modified:
src/stories/assets/css/main.scss— Added@use 'skills'src/stories/assets/css/card.scss— Changed.card--plainpadding to1.25rem 0src/stories/pages/Home.tsx— Imported Skills module, added below Jumbotron with two-tone heading and four skills
- Verified:
npm run build-storybookcompleted successfully after Skills module changes
- Purpose: Full set of themed HTML form elements matching the site's dark design system
- Directory:
src/stories/components/form/— all form components live here, separate from atomic components - Storybook Path:
jorgecalderon.codes > Components > Form > [component] - Shared Styles: Single Sass partial
_form.scsswith@use 'variables' as *;, added tomain.scss - Design Tokens:
$form-radius: 10px— matches%btn-baseborder-radiusinbutton.scss$form-bg: $charcoal— input backgrounds$form-border-focus: $azure-bolt— focus ring color$form-text: $quicksilver— input text$form-placeholder: $pewter— placeholder text- Focus state:
border-color: $azure-bolt+box-shadow: 0 0 0 3px rgba($azure-bolt, 0.25)
- Components Created:
| Component | File | Stories | Key Details |
|---|---|---|---|
| Input | Input.tsx |
7 (Default, Email, Password, WithHelperText, Error, Disabled, NoLabel) | Supports text, email, password, number, search, tel, url types; error state via --error modifier |
| Textarea | Textarea.tsx |
4 (Default, WithHelperText, CustomRows, Disabled) | Configurable rows, vertical resize only |
| Select | Select.tsx |
5 (Default, WithHelperText, Preselected, Disabled, WithDisabledOption) | Custom SVG dropdown arrow (Pewter, turns Azure Bolt on focus), appearance: none |
| Checkbox | Checkbox.tsx |
5 (Default, Checked, Disabled, DisabledChecked, Group) | Custom checkmark via ::after pseudo-element, Azure Bolt checked state |
| Radio | Radio.tsx |
4 (Default, Preselected, Disabled, WithDisabledOption) | Radio group with fieldset/legend, inner dot via ::after, Azure Bolt checked state |
| Toggle | Toggle.tsx |
5 (Default, Checked, Disabled, DisabledChecked, Group) | Custom track/thumb switch, role="switch", thumb translates on check |
- Controlled/Uncontrolled Fix: Checkbox, Radio, and Toggle were initially fully controlled (passing
checkedalways), which prevented visual toggling in Storybook. Fixed by conditionally spreading{ checked }only when the prop is explicitly provided (!== undefined), allowing uncontrolled behavior in stories. - All stories render on Obsidian background (
#1A1A1A) to match the Home page context - Files Created:
src/stories/components/form/Input.tsx+Input.stories.tsxsrc/stories/components/form/Textarea.tsx+Textarea.stories.tsxsrc/stories/components/form/Select.tsx+Select.stories.tsxsrc/stories/components/form/Checkbox.tsx+Checkbox.stories.tsxsrc/stories/components/form/Radio.tsx+Radio.stories.tsxsrc/stories/components/form/Toggle.tsx+Toggle.stories.tsxsrc/stories/assets/css/_form.scss
- Files Modified:
src/stories/assets/css/main.scss— Added@use 'form'
- Purpose: Contact form section displayed below Skills on the Home page
- Component:
ContactForminmodules/— assembles Input, Textarea, and Button into a two-column layout - Layout: Bootstrap grid
row g-4withcol-lg-7(form) andcol-lg-5(sidebar)- Form column: Name + Email side-by-side (
col-md-6each), Subject full-width, Message textarea (6 rows), Send Message button (Azure Bolt) - Sidebar column: Azure Bolt left border accent on content wrapper
- Form column: Name + Email side-by-side (
- Heading: Two-tone
<h2>matching Skills/Jumbotron pattern- "Get in" uses
$deep-azure(.contact-form__heading-accent) - "Touch." uses
$frost(.contact-form__heading-light)
- "Get in" uses
- Sidebar Content — Three Sections:
- Intro list (
introItemsprop) — Icon + text items with Deep Azure circular icon backgrounds (2.25rem), bold Quicksilver text. Example items: "Need help with a project?" (lightbulb), "Got ideas?" (rocket), "Want to chat Drupal?" (Drupal icon) - Closing paragraph (
introClosingprop) — Pewter text, lighter weight. "Or just want to say hello? Drop me a message and let's get started." - Contact details (
detailsprop) — Simple inline icon + text rows. Azure Bolt icons, Sky Blue links (hover → Azure Bolt). Items: email, GitHub, LinkedIn, location
- Intro list (
- Props:
heading?: React.ReactNodeintroItems?: IntroItem[]—{ icon: string, text: string }introClosing?: stringdetails?: ContactDetail[]—{ icon: string, text: string, href?: string }onSubmit?: (e: React.FormEvent<HTMLFormElement>) => void
- Background:
$obsidianon the section - Sidebar alignment: Top-aligned with form labels (not vertically centered)
- Stories:
Default(full sidebar with intro list, closing, and details) andNoSidebar(form only) - Files Created:
src/stories/modules/ContactForm.tsxsrc/stories/modules/ContactForm.stories.tsxsrc/stories/assets/css/_contact-form.scss
- Files Modified:
src/stories/assets/css/main.scss— Added@use 'contact-form'src/stories/pages/Home.tsx— Imported ContactForm, added below Skills with full sidebar content
- Purpose: Editorial-style showcase of work projects with alternating image/text layout, displayed between Skills and ContactForm on the Home page
- Component:
WorkingOninmodules/— acceptsheading(React.ReactNode) andprojectsarray - Layout: Full-width rows with screenshot on one side and text on the other, alternating sides per project via Bootstrap
flex-row-reverseon odd-indexed items- Project 1 (Publishing Options): Image left, text right
- Project 2 (Blood Cancer United): Text left, image right
- Mobile: stacks vertically (image on top, text below)
- Heading: Two-tone
<h2>matching Skills/Jumbotron/ContactForm pattern- "What I'm" uses
$deep-azure(.working-on__heading-accent) - "Working On." uses
$frost(.working-on__heading-light)
- "What I'm" uses
- Project Content Per Item:
- Type label — uppercase, letterspaced,
$pewter,0.75rem(e.g. "Contributed Drupal Module", "Full-Time Role") - Title —
$sky-blue, Manrope,1.35rem,font-weight: 600 - Description —
$quicksilver, Noto Sans,0.95rem,line-height: 1.6 - Button — optional
<a>tag usingbtn btn-azure-boltclasses, opens in new tab (target="_blank")
- Type label — uppercase, letterspaced,
- Image Treatment:
width: 100%,border-radius: 8px, subtlergba($quicksilver, 0.1)border - Background:
$charcoalon the section - Props:
heading?: React.ReactNodeprojects: Project[]—{ title, description, type, imageSrc, imageAlt?, buttonLabel?, buttonHref? }
- Project Data:
- Publishing Options: Contributed Drupal module for custom publishing options, Views integration, D10/D11 compatible. Button: "View Module" → drupal.org/project/pub_options
- Blood Cancer United: Digital platform supporting patients through research, financial assistance, and advocacy. Button: "Visit Site" → bloodcancerunited.org
- Stories:
Default(with two-tone heading and both projects) andNoHeadingvariant - Placeholder Images: SVG placeholders created at
src/stories/assets/pub-options-screenshot.svgandsrc/stories/assets/bcu-screenshot.svg— to be replaced with actual screenshots - Files Created:
src/stories/modules/WorkingOn.tsxsrc/stories/modules/WorkingOn.stories.tsxsrc/stories/assets/css/_working-on.scsssrc/stories/assets/pub-options-screenshot.svgsrc/stories/assets/bcu-screenshot.svg
- Files Modified:
src/stories/assets/css/main.scss— Added@use 'working-on'src/stories/pages/Home.tsx— Imported WorkingOn, placed between Skills and ContactForm
- Purpose: Site-wide footer with navigation links, social icons, and copyright
- Component:
Footerinlayout/— alongside Header as a structural element - Layout:
- Top row: Nav links (left) + social icon circles (right) — stacks centered on mobile
- Divider: subtle
rgba($quicksilver, 0.08)horizontal rule - Bottom row: Copyright (left) + "Built with" text (right)
- Social Icons: Circular buttons (
2.25rem) with$pewtericons and subtle border, hover → Azure Bolt with tinted background - Nav Links:
$pewtertext, hover →$azure-bolt - Copyright:
© {year} Jorge Calderon. All rights reserved. - Built-with: Muted
rgba($pewter, 0.6)text - Background:
$onyx(#212528) — matching the Jumbotron background, no top border - Props:
navItems?: string[](defaults to Home, Blog, Portfolio, Contact)socials?: SocialLink[]—{ icon, href, label }email?: stringname?: string(defaults to "Jorge Calderon")builtWith?: string
- Stories:
Default(full footer with nav, socials, email, built-with) andMinimal(name only) - Files Created:
src/stories/layout/Footer.tsxsrc/stories/layout/Footer.stories.tsxsrc/stories/assets/css/_footer.scss
- Files Modified:
src/stories/assets/css/main.scss— Added@use 'footer'src/stories/pages/Home.tsx— Imported Footer, added below ContactForm
- Color Value:
#212528 - Variable Name:
$onyx - Position in Palette: Between
$obsidianand$charcoal(darkest to lightest) - Usage: Footer background, matches Jumbotron background color
- Files Modified:
src/stories/assets/css/_variables.scss— Added$onyxvariable
- Changed: Skills, Working On, and Contact Form sections from varying padding to consistent
4rem 0- Skills:
2rem 0→4rem 0 - Working On:
3rem 0→4rem 0 - Contact Form:
2rem 0→4rem 0
- Skills:
- Unchanged: Jumbotron (
120px 0) and Footer (2.5rem 0 1.5rem) kept as-is - Files Modified:
src/stories/assets/css/_skills.scsssrc/stories/assets/css/_working-on.scsssrc/stories/assets/css/_contact-form.scss
- Page sections (top to bottom): Header → Jumbotron → Skills → WorkingOn → ContactForm → Footer
- ContactForm content on Home page:
- Intro items: lightbulb ("Need help with a project?"), rocket ("Got ideas?"), Drupal ("Want to chat Drupal?")
- Closing: "Or just want to say hello? Drop me a message and let's get started."
- Details: email (hello@jorgecalderon.codes), GitHub, LinkedIn, location (Florida, USA)
- Footer content on Home page:
- Socials: GitHub, LinkedIn, Drupal.org
- Email: hello@jorgecalderon.codes
- Built with: "React, Storybook & Drupal"
- Verified:
npm run build-storybookcompleted successfully after all WorkingOn module, Footer, section padding, and Home page integration changes
- Created: New Charcoal card variant for use on Obsidian backgrounds
- Design Rationale: Needed a dark card variant that stands out against dark Obsidian page backgrounds
- Implementation:
- Added
CardVarianttype:'default' | 'charcoal' - Added
variantprop to Card component with default value'default' - Created
.card--charcoalmodifier class incard.scss - Solid charcoal background (
$charcoal) - removed gradients per user feedback - Sky Blue title color (
$sky-blue) - changed from gradient to solid color for simplicity - Ash body text (
$ash) - Azure Bolt bottom border accent
- Subtle border:
rgba(226, 226, 226, 0.15)
- Added
- Files Modified:
src/stories/components/Card.tsx- Added variant prop and logicsrc/stories/assets/css/card.scss- Added Charcoal variant stylessrc/stories/components/Card.stories.tsx- Added variant control to argTypes
- Created: Complete set of Charcoal variant stories matching all default card patterns
- Stories Added:
CharcoalTextOnly- Title + body textCharcoalTitleOnly- Title aloneCharcoalWithImage- Title + text + imageCharcoalWithButton- Title + text + Azure Bolt buttonCharcoalWithImageAndButton- Full card with image and buttonCharcoalTitleAndButton- Compact card with title and buttonCharcoalButtonDeepAzure- Card with Deep Azure buttonCharcoalButtonQuicksilver- Card with Quicksilver button
- Story Parameters: All Charcoal stories display on Obsidian background (
#1A1A1A) for proper contrast - File:
src/stories/components/Card.stories.tsx
- Color Value:
#75B8E6 - Variable Name:
$sky-blue - Description: Light accent color
- Usage: Card titles in Charcoal variant
- Files Modified:
src/stories/assets/css/_variables.scss- Added$sky-bluevariablesrc/stories/design-specs/ColorPalette.tsx- Added toquicksilverColorsarraysrc/stories/design-specs/ColorPalette.stories.tsx- AddedSkyBluestory
- Iteration 1: Azure Bolt solid color
- Iteration 2: Gradient from Sky Blue to Frost
- Iteration 3: Three-color gradient (Deep Azure → Sky Blue → Frost)
- Iteration 4: Adjusted gradient stops - Sky Blue at 45% (22.5% to 67.5%)
- Iteration 5: Adjusted gradient stops - Sky Blue at 60% (20% to 80%)
- Final Decision (User): Reverted to solid Sky Blue color for simplicity
- User commented out gradient code in
card.scss - Title now uses
color: $sky-blue;instead of gradient
- User commented out gradient code in
- Reason: User feedback - "It looks ugly"
- Removed From:
CardVarianttype (removed'pewter'option)card.scss(removed.card--pewterstyles)Card.stories.tsx(removed Pewter variant stories and comparison)Card.stories.tsxargTypes (removed from variant options)
- Kept: Only
'default'and'charcoal'variants remain
- Added: Sky Blue to Design Specs color palette
- Display: Shows in both main Quicksilver palette story and individual SkyBlue story
- Purpose: Documents the new light accent color for reference
- Gradient vs Solid Color: User preferred solid Sky Blue over complex gradients for title
- Background Simplicity: Solid charcoal background instead of gradients for cleaner look
- Variant System: Established pattern for creating card variants using BEM modifiers
- Story Organization: Grouped all Charcoal variant stories together with consistent naming
- Verified:
npm run build-storybookcompleted successfully multiple times throughout session - All Variants: Both default and Charcoal variants render correctly in Storybook
- Created: New
src/stories/modules/directory for larger, composite components - Moved Components: Relocated Jumbotron, BlogPostGrid, and AboutMe from
components/tomodules/ - Rationale: These are composite sections that combine multiple smaller components, distinct from atomic UI components like Button, Card, Label, Divider
- Files Moved:
Jumbotron.tsx+Jumbotron.stories.tsxBlogPostGrid.tsx+BlogPostGrid.stories.tsxAboutMe.tsx+AboutMe.stories.tsx
- Updated: All import statements to reflect new module locations
- Module Internal Imports: Changed from
./Componentto../components/ComponentAboutMe.tsx: Imports Divider, Label, Button, Card from../components/BlogPostGrid.tsx: Imports Card, Divider from../components/Jumbotron.tsx: Imports Button, Card, Divider from../components/
- Page Imports: Updated
Home.tsxto import Jumbotron from../modules/Jumbotron - Storybook Titles: Changed story titles from
Components/*toModules/*
- Issue: Azure Bolt button text was turning black on hover
- Fix: Added explicit
color: $quicksilver;to hover states for all dark button variants - Affected Variants:
.btn-azure-bolt- Addedcolor: $quicksilver;to:hover.btn-deep-azure- Addedcolor: $quicksilver;to:hover.btn-obsidian- Addedcolor: $quicksilver;to:hover.btn-charcoal- Addedcolor: $quicksilver;to:hover
- Unchanged:
.btn-quicksilveralready hadcolor: $obsidian;in hover state - File:
src/stories/assets/css/button.scss
- Updated:
CLAUDE.mdto reflect new project structure - Added:
modules/directory to project layout documentation - Description: "Larger, composite sections (Jumbotron, BlogPostGrid, AboutMe) + stories"
- Verified:
npm run build-storybookcompleted successfully - Output: All stories properly organized under new Modules section in Storybook
- Changed: Nav menu items (desktop & mobile) to match Azure Bolt button styling
- Implementation:
- Added
@use 'sass:color'toheader.scss - Background:
linear-gradient(135deg, color.adjust($azure-bolt, $lightness: 5%), $azure-bolt, color.adjust($azure-bolt, $lightness: -5%)) - Border-bottom:
5px solid transparent(base) →$deep-azure(hover) - Removed box-shadow from nav pills (user requested no drop shadow on text)
- Removed
border-bottom-colorfrom transition list so border snaps instantly with background
- Added
- Added: Staggered fade-in for desktop menu items on page load
- Implementation:
- Items start at
opacity: 0 - Cascade timing: Home (0.2s) → Blog (0.3s) → Portfolio (0.4s) → Contact (0.5s)
- Pure opacity animation (no movement) via
@keyframes header-item-fade-in - No fade-out on mouse interactions
- Items start at
- File:
src/stories/assets/css/header.scss
- Changed: Card background from light gradient to metallic sheen effect
- Design: Light metallic (not dark) - bright white highlight sweeping diagonally
- Implementation:
- Layered gradients: white sheen overlay (0.9 → 0.4 opacity) + base gradient (white → quicksilver → ash → pewter)
- Sheen positioned bottom-right via
background-position - Colors:
$obsidian(title),$charcoal(text),rgba($pewter, 0.3)(footer border)
- Files:
src/stories/assets/css/card.scss
- Added: Animated metallic sheen sweep on card hover
- Implementation:
- 0.4s pause, then 1s smooth sweep left across card
- Uses
background-positionanimation (200% sized gradient) @keyframes card-sheen-sweepwith hold at 0-28%, sweep to 100%- No reverse animation on mouse-leave (instant snap back)
- File:
src/stories/assets/css/card.scss
- Content Changes:
- Heading: "Hi, I'm Jorge / I build Drupal."
- Bio trimmed to ~50 words focusing on DevOps, design, AI usage, family
- Featured card: "Designing with AI in Storybook" (imgStyling image)
- Labels: AI, React, Sass
- Layout:
- Removed BlogPostGrid component
- Navigation updated to: Home, Blog, Portfolio, Contact
- Background:
$obsidianwithmin-height: 100vh
- Files:
src/stories/pages/Home.tsx,src/stories/assets/css/pages.scss
- Ambient Glow:
- Added subtle glow ball (quicksilver at 0.026/0.004 opacity) via
::afterpseudo-element - Positioned left side at
-180px, 600x600px radial gradient - Multiple opacity iterations to achieve subtle effect
- Added subtle glow ball (quicksilver at 0.026/0.004 opacity) via
- Divider Animation:
- Simplified to linear 0→25% width expansion (removed bounce)
- Timing: 1.2s ease-out with 0.4s delay
- Margins:
0.3125rem 0 0.5625rem(top/bottom adjusted multiple times)
- Button Alignment:
- Left column:
justify-content-startwith buttons usingmt-auto - Buttons rise on hover:
translateY(-3px)(no bounce animation)
- Left column:
- Files:
src/stories/assets/css/jumbotron.scss,src/stories/components/Jumbotron.tsx
- Created:
Label.mdxcomponent documentation page - Fix: Removed
tags: ['autodocs']fromLabel.stories.tsxto prevent 404 conflict with MDX
- BEM Naming:
.block__element--modifierthroughout - Sass Partials: Every partial that uses palette variables must include
@use 'variables' as *; - Bootstrap Integration: Pre-built CSS (no Sass compilation), avoid
!importantconflicts - Animation Approach:
- Use
@keyframeswithforwardsfor load animations - Use
transitionfor interactive hover states - Instant snaps for properties that shouldn't ease (font-weight, border-color)
- Use
- Gradient Animations: Use
background-positionon oversized gradients (can't animate gradient angles directly)
- Nav/Header:
$obsidianbackground,$quicksilvertext,$azure-boltaccents/active states - Cards (Default): Metallic gradient from white →
$pewter,$obsidiantitles,$charcoalbody text - Cards (Charcoal): Solid
$charcoalbackground,$sky-bluetitles,$ashbody text, for use on$obsidianbackgrounds - Cards (Plain): Transparent background,
$quicksilvertitles (600 weight),$pewterdescription and icon, horizontal layout, no borders/shadow/hover — for skill showcases on$obsidianbackgrounds - Form Inputs:
$charcoalbackground,$quicksilvertext,$pewterplaceholders,$azure-boltfocus border + ring,10pxborder-radius (matching buttons) - ContactForm Sidebar: Azure Bolt left border, Deep Azure circular icon backgrounds,
$quicksilverintro text,$pewterclosing text, Sky Blue links - Buttons: Azure Bolt uses
color.adjust()for lightness variations - WorkingOn:
$charcoalbackground,$sky-bluetitles,$quicksilverdescriptions,$pewtertype labels (uppercase), alternating image/text rows with Azure Bolt buttons - Footer:
$onyxbackground,$pewternav links and social icons with circular borders, hover →$azure-bolt, muted copyright and built-with text - Page Background:
$obsidianfor dark themes
- Fast interactions:
$transition-fast(0.2s) for hovers - Card sheen: 0.4s pause + 1s sweep
- Nav fade-in: 0.4s duration, staggered 0.1s between items
- Divider expand: 1.2s ease-out with 0.4s delay
- Missing
@use 'variables': Causes infinite preview spinner (Sass undefined variable error) - Bootstrap
w-25: Uses!important, blocks max-width animations - must animatewidthdirectly - Story file extensions:
.tsfor CSF3 args-only,.tsxwhen using JSX/render functions - MDX vs autodocs: Can't have both
tags: ['autodocs']in stories AND a.mdxfile
src/stories/components/Card.tsx- Added CardVariant type, variant prop, variant class logicsrc/stories/assets/css/card.scss- Added Charcoal variant styles (solid background, Sky Blue title)src/stories/components/Card.stories.tsx- Added variant argTypes, created 8 Charcoal variant storiessrc/stories/assets/css/_variables.scss- Added$sky-blue: #75B8E6src/stories/design-specs/ColorPalette.tsx- Added Sky Blue to quicksilverColors arraysrc/stories/design-specs/ColorPalette.stories.tsx- Added SkyBlue story
- Multiple files for project restructure (modules directory)
src/stories/assets/css/button.scss- Hover color fixesCLAUDE.md- Documentation updates
src/stories/assets/css/header.scss- Nav styling, fade-in animationsrc/stories/assets/css/card.scss- Metallic background, sheen animationsrc/stories/assets/css/jumbotron.scss- Glow ball, divider animation, marginssrc/stories/assets/css/pages.scss- Obsidian backgroundsrc/stories/assets/css/button.scss- Subtle rise on hover (removed bounce)src/stories/pages/Home.tsx- Content updates, layout changessrc/stories/components/Jumbotron.tsx- Button alignment, removedw-25from dividersrc/stories/components/Label.mdx- Createdsrc/stories/components/Label.stories.tsx- Removed autodocs tagsrc/stories/components/Jumbotron.stories.tsx- Updated featured card content
- Reason: Replaced by the new BlogList module which uses the Skills-style plain card layout
- Files Deleted:
src/stories/modules/BlogPostGrid.tsxsrc/stories/modules/BlogPostGrid.stories.tsxsrc/stories/assets/css/blog-posts.scss
- Files Modified:
src/stories/assets/css/main.scss— Removed@use 'blog-posts'
- Purpose: Hero-style featured blog post with image/text side-by-side layout, used at the top of the Blog page
- Component:
BlogFeaturedinmodules/— acceptsheading(React.ReactNode) andpost(FeaturedPost) - Layout: Single row — image left (
col-12 col-md-6), content right (col-12 col-md-6), matching the WorkingOn layout pattern - FeaturedPost interface:
title,excerpt,date,imageSrc,imageAlt?,labels?: CardLabel[],buttonLabel?,buttonHref? - Content Per Item:
- Date — uppercase, letterspaced,
$pewter,0.75rem(same style as WorkingOn's type label) - Title —
$sky-blue, Manrope,1.35rem,font-weight: 600 - Labels — rendered using Label component with auto-mapped tech colours
- Excerpt —
$quicksilver, Noto Sans,0.95rem,line-height: 1.6 - Button — optional
<a>usingbtn btn-azure-boltclasses
- Date — uppercase, letterspaced,
- Heading: Two-tone
<h2>— "Latest" ($deep-azure) + "Post." ($frost) - Divider Animation: Animated expand from 0% to 25% width (
blog-featured-divider-expandkeyframes, 1.2s ease-out, 0.4s delay). Bootstrapw-25class removed to avoid!importantconflict blocking the animation. - Background:
$obsidian - Stories:
Default(with two-tone heading) andNoHeading - Files Created:
src/stories/modules/BlogFeatured.tsxsrc/stories/modules/BlogFeatured.stories.tsxsrc/stories/assets/css/_blog-featured.scss
- Files Modified:
src/stories/assets/css/main.scss— Added@use 'blog-featured'
- Purpose: 2-column grid of blog post listings below the featured post on the Blog page
- Component:
BlogListinmodules/— acceptsheading(React.ReactNode) andpostsarray - Layout: Skills-style 2-column grid (
row g-0,col-12 col-md-6) using plain Card variant - BlogListPost interface:
icon,title,description,imageSrc?,imageAlt?,href? - Thumbnail Support: Each post has an optional 80×80px rounded thumbnail image to the left of the plain card
- Clickable Posts: When
hrefis provided, the entire post block (thumbnail + card) is wrapped in an<a>tag (.blog-list__link) - Hover Effect: Background transitions to
$obsidianon hover (section background is$charcoal) - Text Colour Preservation: Inside the link wrapper, explicit colours set on
.card__title($quicksilver),.card__text($pewter),.card__icon($pewter) to prevent<a>tag colour inheritance - Heading: Two-tone
<h2>— "More" ($deep-azure) + "Posts." ($frost) - Background:
$charcoal - Design Iterations:
- Started using BlogPostGrid (3-column card grid with images) — user rejected
- Switched to Skills-style plain cards (no images) — user wanted images
- Added thumbnail images alongside plain cards — user wanted button links
- Added button links in a separate row — user wanted whole block clickable instead
- Removed buttons, made entire post block an
<a>tag with obsidian hover background
- Stories:
Default(with two-tone heading) andNoHeading - Files Created:
src/stories/modules/BlogList.tsxsrc/stories/modules/BlogList.stories.tsxsrc/stories/assets/css/_blog-list.scss
- Files Modified:
src/stories/assets/css/main.scss— Added@use 'blog-list'
- Purpose: Blog landing page with featured post hero and post listing grid
- Component:
Bloginpages/— acceptsactiveMenuItem(default'Blog') - Page sections (top to bottom): Header → BlogFeatured → BlogList → Footer
- BlogFeatured content: "Designing with AI in Storybook" (February 2026), labels: AI, React, Sass
- BlogList content: 4 posts with thumbnails and links — Drupal Publishing Options, DevOps Pipelines, Component-Driven Design, Migrating to Drupal 11
- Footer: Same configuration as Home page (GitHub, LinkedIn, Drupal.org socials)
- Stories:
Defaultatjorgecalderon.codes/Pages/Blog - Files Created:
src/stories/pages/Blog.tsxsrc/stories/pages/Blog.stories.tsx
- Files Modified:
src/stories/assets/css/pages.scss— Added.page-blogclass ($obsidianbackground,min-height: 100vh)
- Purpose: Hero-style featured project with image/text side-by-side layout, used at the top of the Portfolio page
- Component:
PortfolioFeaturedinmodules/— acceptsheading(React.ReactNode) andproject(FeaturedProject) - Layout: Mirrors BlogFeatured — single row, image left, content right
- FeaturedProject interface:
title,description,type,imageSrc,imageAlt?,labels?: CardLabel[],buttonLabel?,buttonHref? - Content Per Item:
- Type label — uppercase, letterspaced,
$pewter(e.g. "Contributed Drupal Module") - Title —
$sky-blue, Manrope,1.35rem,font-weight: 600 - Labels — rendered using Label component
- Description —
$quicksilver, Noto Sans,0.95rem - Button — optional
<a>usingbtn btn-azure-bolt
- Type label — uppercase, letterspaced,
- Heading: Two-tone
<h2>— "Featured" ($deep-azure) + "Project." ($frost) - Divider Animation: Same expand pattern as BlogFeatured (
portfolio-featured-divider-expand, 0→25%, 1.2s ease-out, 0.4s delay) - Background:
$obsidian - Stories:
DefaultandNoHeading - Files Created:
src/stories/modules/PortfolioFeatured.tsxsrc/stories/modules/PortfolioFeatured.stories.tsxsrc/stories/assets/css/_portfolio-featured.scss
- Files Modified:
src/stories/assets/css/main.scss— Added@use 'portfolio-featured'
- Purpose: Alternating image/text rows showcasing portfolio projects, used below the featured project on the Portfolio page
- Component:
PortfolioListinmodules/— acceptsheading(React.ReactNode) andprojectsarray - Layout: Mirrors WorkingOn module — full-width rows with screenshot on one side and text on the other, alternating sides via
flex-row-reverseon odd-indexed items - PortfolioProject interface:
title,description,type,imageSrc,imageAlt?,buttonLabel?,buttonHref? - Content: Same typography and spacing as WorkingOn (type label, sky-blue title, quicksilver description, optional azure-bolt button)
- Heading: Two-tone
<h2>— "More" ($deep-azure) + "Projects." ($frost) - Background:
$charcoal - Stories:
DefaultandNoHeading - Sample Data: Blood Cancer United (full-time role), jorgecalderon.codes (personal project), Enterprise CMS Platform (client project) — using project placeholder SVGs
- Files Created:
src/stories/modules/PortfolioList.tsxsrc/stories/modules/PortfolioList.stories.tsxsrc/stories/assets/css/_portfolio-list.scss
- Files Modified:
src/stories/assets/css/main.scss— Added@use 'portfolio-list'
- Purpose: Portfolio landing page with featured project hero and project listing
- Component:
Portfolioinpages/— acceptsactiveMenuItem(default'Portfolio') - Page sections (top to bottom): Header → PortfolioFeatured → PortfolioList → Footer
- PortfolioFeatured content: Publishing Options (Contributed Drupal Module), labels: Drupal, PHP, button: "View Module" → drupal.org
- PortfolioList content: 3 projects with alternating layout — BCU, jorgecalderon.codes, Enterprise CMS
- Footer: Same configuration as Home and Blog pages
- Stories:
Defaultatjorgecalderon.codes/Pages/Portfolio - Files Created:
src/stories/pages/Portfolio.tsxsrc/stories/pages/Portfolio.stories.tsx
- Files Modified:
src/stories/assets/css/pages.scss— Added.page-portfolioclass ($obsidianbackground,min-height: 100vh)
- Issue: Divider expand animation did not play on the BlogFeatured section
- Root Cause: Bootstrap's
w-25utility class setswidth: 25% !important, overriding the@keyframesanimation starting atwidth: 0 - Fix: Removed
w-25class from the Divider in BlogFeatured (and PortfolioFeatured), letting the@keyframesanimation control width from 0% to 25% - Pattern: Any section that needs an animated divider must NOT use Bootstrap width utilities — use
@keyframesdirectly
- Verified:
npm run build-storybookcompleted successfully after all Blog page, Portfolio page, and module changes
- Replace WorkingOn placeholder SVGs with actual screenshots of drupal.org/project/pub_options and bloodcancerunited.org
- Add Onyx color to Design Specs ColorPalette component and stories
- Contact page with form (reuse ContactForm module)
- Responsive behavior verification across breakpoints
- Accessibility audit (ARIA labels, keyboard nav, color contrast)
- Performance: consider reducing animation complexity on mobile
- Update CLAUDE.md project layout to include new modules and pages