Skip to content

Add AccordionComponent - #7

Open
PendragonDevelopment wants to merge 5 commits into
fix/lookbook-button-previewsfrom
feat/accordion-component
Open

Add AccordionComponent#7
PendragonDevelopment wants to merge 5 commits into
fix/lookbook-button-previewsfrom
feat/accordion-component

Conversation

@PendragonDevelopment

Copy link
Copy Markdown
Collaborator

Summary

Adds `Shipwright::AccordionComponent` — a single collapsible section matching Shipwright Pro's Accordion atom (Figma node `2013:5693`). Uses native HTML `

`/`` so there's no JavaScript to write, ship, or debug.

Stack order:

API

```erb
<%= render(Shipwright::AccordionComponent.new(title: "FAQ question")) do %>
Answer content here.
<% end %>

<%# Start expanded %>
<%= render(Shipwright::AccordionComponent.new(title: "T", open: true)) { "..." } %>
```

Param Values Default
`title` String (required)
`open` `true`/`false` `false`
`class` Any Tailwind classes `nil`

Content block → body text rendered below the summary.

Why `
Details` over JS?

  • Zero JS. Toggle, keyboard (Enter/Space), screen-reader expanded state, focus management — all native.
  • Chevron animation via Tailwind's `group-open:rotate-180` modifier. No React-style `isOpen` state to synchronize.
  • Works without Stimulus/Turbo/hotwire, inside any Rails stack.

For groups: stack N AccordionComponents. No "only one open" constraint today; if needed later, consumers can add a tiny Stimulus controller that closes siblings on open.

New tokens

```
--color-border-primary: #eaeaea /* subtle gray borders /
--color-background-secondary: #f3f3f3 /
hover surface */
```

MERGER config updated for both.

Test plan

  • 12 new tests pass (title, body, open state, chevron rotation, hover/focus classes, marker hidden, class override, HTML attrs)
  • Full suite: 64 tests, 141 assertions, 0 failures
  • `rake tailwindcss:build` compiles new tokens + `group-open:rotate-180`
  • `/components` renders a stacked group with one initially-open item
  • Lookbook at `/lookbook/preview/shipwright/accordion/*` with 4 scenarios

🤖 Generated with Claude Code

New Shipwright::IconComponent renders inline SVG icons from a manifest
of Feather Icons (https://feathericons.com, MIT licensed). Matches
Shipwright Pro's Figma icon set (node 2:163).

Starter manifest includes 21 commonly-needed icons:
info, alert-circle, alert-triangle, check, check-circle, x, x-circle,
chevron-{up,down,left,right}, plus, minus, search, menu, external-link,
settings, arrow-{left,right}, help-circle, trash.

Icons live in app/components/shipwright/icons.rb as a frozen constant
hash of name => inner-SVG fragment. Adding a new icon = paste Feather's
inner markup into the hash.

API:
  <%= render Shipwright::IconComponent.new(name: :info) %>
  <%= render Shipwright::IconComponent.new(name: :check, size: :lg, class: "text-utility-negative-default") %>
  <%= render Shipwright::IconComponent.new(name: :info, "aria-label": "More info") %>

Props:
- name: required symbol/string matching a key in ICONS
- size: :sm (16px), :md (24px, default), :lg (32px)
- class: consumer override
- **html_attrs: pass-through (id, data-*, aria-*, etc.)

Icons use stroke="currentColor" so they inherit the parent's text color —
consumers control color via text-* utilities. Decorative by default
(aria-hidden=true); providing aria-label makes the icon meaningful and
skips aria-hidden.

13 new component tests pass. Total suite: 38 tests, 81 assertions.
Lookbook previews: default, gallery (all 21), sizes, colored,
with_aria_label.
Generated from feather-icons@4.29.2 icons.json plus two Shipwright
additions to match Figma's icon page (node 2:163):
- code-horizontal: alias for Feather's 'code' (matches Shipwright
  Pro's naming convention)
- star-filled: filled variant using the star polygon with
  fill=currentColor

Previously only 21 starter icons were shipped. This adds the remaining
~270 to cover every icon on the Shipwright Pro 'Icons / General' page.

Brand icons (payment methods at node 2:824 and social icons at
2:940) are multi-color composite SVGs served from Figma's temporary
CDN and need a different rendering approach — they'll ship in a
follow-up BrandIconComponent PR.

Gallery preview updated to render all icons in an 8-column grid.
Showcase page shows a curated sample plus pointer to Lookbook for
the full set.

All 38 existing tests pass (Icon manifest expansion preserves the
starter icons we already shipped).
Brand icons (payment methods + social platforms) from Shipwright Pro's
Figma are multi-color composite SVGs that need different rendering than
Feather's monochrome stroke icons. Added as a parallel component:

  <%= render Shipwright::BrandIconComponent.new(name: :visa-color) %>

Architecture:
- SVGs live in app/assets/images/shipwright/brand/ (one file per icon)
- Component loads and memoizes the directory on first access
- Renders SVG inline (preserves brand colors, allows CSS styling)
- Strips intrinsic width/height, applies h-* class + w-auto so
  non-square icons (e.g., Visa) keep their aspect ratio
- Raises a helpful ArgumentError directing users to the exporter
  if they reference a missing icon

New files:
- script/export_brand_icons.rb: downloads all 59 brand SVGs from
  Shipwright Pro Figma via the Figma REST API. Maps every node ID
  from the Figma Icons/Payment Method and Icons/Social pages to a
  normalized kebab-case filename. Requires FIGMA_ACCESS_TOKEN env var.
- app/components/shipwright/brand_icon_component.rb: the component.
- test fixtures at test/fixtures/brand_icons/ so tests don't need
  Figma access. BrandIconComponent.asset_path is swappable per-test.
- 4 Lookbook previews including an empty_state scenario explaining
  how to run the exporter.

14 new component tests pass. Total suite: 52 tests, 111 assertions.

Usage:
  FIGMA_ACCESS_TOKEN=xxx ruby script/export_brand_icons.rb
  # Review, commit the SVGs. 59 icons covering:
  #   Payment: amex, visa, mastercard, applepay, paypal, discover,
  #            cash, cash-dollar, card-default (color/fill/outline)
  #   Social:  facebook, instagram, youtube, google, linkedin, apple,
  #            snapchat, pinterest, medium, angelist, slack, dribbble,
  #            figma, discord, clubhouse, tumblr, telegram, tiktok,
  #            vk, signal, reddit, github, fb-messenger, skype,
  #            spectrum, zoom, facetime, google-meet, behance,
  #            invision, microsoft
Single collapsible section using native HTML <details>/<summary> — no
JavaScript required. Stack multiple AccordionComponents for an FAQ-style
group; native semantics handle toggle, keyboard navigation, and
accessibility out of the box.

API:
  <%= render(Shipwright::AccordionComponent.new(title: "FAQ question")) do %>
    Answer content here.
  <% end %>

  <%# Start expanded %>
  <%= render(Shipwright::AccordionComponent.new(title: "T", open: true)) { "..." } %>

Props:
- title: required string rendered in the summary
- open: bool (default false) — sets the <details open> attribute
- class: consumer override on the <details> element
- **html_attrs: pass-through (id, data-*, etc.)

Content block → body text rendered below the summary.

Styling matches Shipwright Pro Figma (node 2013:5693):
- Default: text-interactive-primary-default, border-border-primary
- Hover: bg-background-secondary, text-interactive-primary-hover
- Focus-visible: 2px ring on interactive-primary-default
- Chevron icon (IconComponent name: :chevron-down) rotates 180deg
  when open via Tailwind's group-open: modifier — works natively with
  <details open>, no JS state management
- Default summary marker hidden cross-browser via list-none +
  ::-webkit-details-marker

New tokens:
- --color-border-primary: #eaeaea (subtle gray borders, matches
  Shipwright Pro Border/Primary)
- --color-background-secondary: #f3f3f3 (hover surface, matches
  Shipwright Pro Interactive/Secondary-Hover)

BaseComponent MERGER config updated with both new color tokens.

Uses IconComponent for the chevron (branch includes the icon-component
merge for that dependency).

12 new tests pass. Total suite: 64 tests, 141 assertions.
Lookbook previews: default, initially_open, stacked_group, long_body.
Animates open/close via the native <details> element's ::details-content
pseudo-element, combined with interpolate-size: allow-keywords to enable
transitioning to/from height: auto. No JavaScript required.

- [&::details-content]:h-0 when collapsed, h-auto when [open]
- 200ms transition on height + content-visibility (the latter needs
  transition-behavior allow-discrete which Tailwind 4 handles via
  the content-visibility transition entry)
- Chevron rotation timing already matches at 200ms

Body gets pt-2 (8px) for breathing room below the summary — matches
Shipwright Pro's spacing scale.

Browser support: Chromium 129+, Safari 18.2+ (late 2024). Firefox
gracefully falls back to instant toggle until interpolate-size ships.
No a11y impact — <details> still works natively on all browsers.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant