@stoovles/gap-kit 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/index.js +2417 -0
  2. package/dist/styles.css +4309 -0
  3. package/guidelines/components/accordion.md +99 -0
  4. package/guidelines/components/breadcrumb.md +74 -0
  5. package/guidelines/components/buttons.md +90 -0
  6. package/guidelines/components/checkbox.md +86 -0
  7. package/guidelines/components/chip.md +77 -0
  8. package/guidelines/components/divider.md +52 -0
  9. package/guidelines/components/dropdown.md +125 -0
  10. package/guidelines/components/filter-sort.md +108 -0
  11. package/guidelines/components/fulfillment.md +100 -0
  12. package/guidelines/components/global-footer.md +71 -0
  13. package/guidelines/components/global-header.md +67 -0
  14. package/guidelines/components/hamburger-nav.md +79 -0
  15. package/guidelines/components/handle.md +49 -0
  16. package/guidelines/components/icons.md +147 -0
  17. package/guidelines/components/link.md +70 -0
  18. package/guidelines/components/logo.md +63 -0
  19. package/guidelines/components/mega-nav.md +103 -0
  20. package/guidelines/components/notification.md +97 -0
  21. package/guidelines/components/pagination.md +88 -0
  22. package/guidelines/components/popover.md +77 -0
  23. package/guidelines/components/price.md +86 -0
  24. package/guidelines/components/product-card.md +82 -0
  25. package/guidelines/components/radio.md +92 -0
  26. package/guidelines/components/search-input.md +63 -0
  27. package/guidelines/components/selector-swatch.md +78 -0
  28. package/guidelines/components/selector.md +95 -0
  29. package/guidelines/components/slider.md +116 -0
  30. package/guidelines/components/switch.md +108 -0
  31. package/guidelines/components/tabs.md +83 -0
  32. package/guidelines/components/text-input.md +80 -0
  33. package/guidelines/composition/buy-box.md +132 -0
  34. package/guidelines/composition/recommendations.md +100 -0
  35. package/package.json +38 -0
@@ -0,0 +1,108 @@
1
+ # Filter & Sort
2
+
3
+ A two-part filtering system for PLPs: **FilterControls** (horizontal chip bar with active filters) and **FilterDrawer** (a slide-out drawer with accordion sections).
4
+
5
+ ## Usage
6
+
7
+ ### FilterControls
8
+
9
+ ```jsx
10
+ import { FilterControls } from "@stoovles/gap-kit";
11
+
12
+ <FilterControls
13
+ chips={[
14
+ { label: "Filter & Sort", value: "filter-sort" },
15
+ { label: "Category", value: "category" },
16
+ { label: "Size", value: "size" },
17
+ { label: "Color", value: "color" },
18
+ { label: "Model Size", value: "model-size", hasDropdown: true },
19
+ ]}
20
+ activeChip="filter-sort"
21
+ onChipClick={(val) => openFilter(val)}
22
+ activeFilters={[
23
+ { label: "Blue", value: "color-blue" },
24
+ { label: "Medium", value: "size-m" },
25
+ ]}
26
+ onRemoveFilter={(val) => removeFilter(val)}
27
+ onClearAll={() => clearFilters()}
28
+ resultCount="128 Results"
29
+ />
30
+ ```
31
+
32
+ ### FilterDrawer
33
+
34
+ ```jsx
35
+ import { FilterDrawer, Checkbox, CheckboxGroup } from "@stoovles/gap-kit";
36
+
37
+ <FilterDrawer
38
+ open={drawerOpen}
39
+ title="Filter & Sort"
40
+ onClose={() => setDrawerOpen(false)}
41
+ sections={[
42
+ {
43
+ title: "Category",
44
+ count: 12,
45
+ subtitle: "Tops, Dresses",
46
+ children: (
47
+ <CheckboxGroup>
48
+ <Checkbox label="Tops" />
49
+ <Checkbox label="Dresses" />
50
+ <Checkbox label="Jeans" />
51
+ </CheckboxGroup>
52
+ ),
53
+ },
54
+ {
55
+ title: "Size",
56
+ count: 8,
57
+ subtitle: "S, M, L",
58
+ children: <Selector options={sizeOptions} />,
59
+ },
60
+ ]}
61
+ onApply={() => applyFilters()}
62
+ />
63
+ ```
64
+
65
+ ## Props — FilterControls
66
+
67
+ | Prop | Type | Default | Description |
68
+ |------|------|---------|-------------|
69
+ | `chips` | `FilterChip[]` | `[]` | Available filter chips |
70
+ | `activeChip` | `string` | — | Currently active chip value |
71
+ | `onChipClick` | `(value: string) => void` | — | Chip click handler |
72
+ | `activeFilters` | `ActiveFilter[]` | `[]` | Active filter tags |
73
+ | `onRemoveFilter` | `(value: string) => void` | — | Remove a filter |
74
+ | `onClearAll` | `() => void` | — | Clear all filters |
75
+ | `resultCount` | `string` | — | Result count text |
76
+
77
+ ## Props — FilterDrawer
78
+
79
+ | Prop | Type | Default | Description |
80
+ |------|------|---------|-------------|
81
+ | `open` | `boolean` | **required** | Whether the drawer is visible |
82
+ | `title` | `string` | `"Filter & Sort"` | Drawer header title |
83
+ | `onClose` | `() => void` | **required** | Close handler |
84
+ | `sections` | `FilterSection[]` | `[]` | Accordion filter sections |
85
+ | `applyLabel` | `string` | `"Apply"` | Apply button label |
86
+ | `onApply` | `() => void` | — | Apply callback (renders button) |
87
+
88
+ ## Visual reference
89
+
90
+ ### FilterControls
91
+ - **Chips:** Bordered pills, 16px text; active chip gets 2px accent border
92
+ - **"Filter & Sort" chip:** Includes a small filter icon to the left
93
+ - **Active tags:** Compact pills with accent border + X close icon
94
+ - **"Clear all":** Underlined text link
95
+ - **Result count:** Small 10px secondary text
96
+
97
+ ### FilterDrawer
98
+ - **Width:** 320px, slides in from the right
99
+ - **Header:** 20px bold title + close X, 24px padding
100
+ - **Sections:** Each has a subtle divider, bold title with optional (count) and subtitle, chevron toggle
101
+ - **Apply button:** Full-width primary button at the bottom
102
+
103
+ ## Rules
104
+
105
+ - FilterControls and FilterDrawer are separate components; use them together for the full filtering experience.
106
+ - The drawer sections accept any `children` — compose with Checkbox, Selector, SelectorSwatch, RangeSlider, etc.
107
+ - Always show `resultCount` so the user knows how many items match the current filters.
108
+ - The "Filter & Sort" chip value should be `"filter-sort"` to trigger the filter icon rendering.
@@ -0,0 +1,100 @@
1
+ # Fulfillment
2
+
3
+ ## When to use
4
+
5
+ Use `Fulfillment` to let customers choose between shipping and in-store pickup. Each option is a `FulfillmentTile` — a card with a title, description, and availability status. Wrap them in a `Fulfillment` container for side-by-side layout.
6
+
7
+ ```tsx
8
+ import { Fulfillment, FulfillmentTile } from '@stoovles/gap-kit'
9
+ ```
10
+
11
+ ## Props
12
+
13
+ ### FulfillmentTile
14
+
15
+ | Prop | Type | Default | Description |
16
+ |---|---|---|---|
17
+ | `title` | `string` | — | Tile heading (e.g. "Free shipping", "In-store pickup") |
18
+ | `selected` | `boolean` | `false` | Whether this tile is the active fulfillment method |
19
+ | `availability` | `string` | — | Status text (e.g. "In Stock") |
20
+ | `children` | `ReactNode` | — | Subtext, links, store name, etc. |
21
+ | `onClick` | `() => void` | — | Selection handler |
22
+ | `className` | `string` | — | Additional CSS class |
23
+
24
+ ### Fulfillment
25
+
26
+ | Prop | Type | Default | Description |
27
+ |---|---|---|---|
28
+ | `children` | `ReactNode` | — | `FulfillmentTile` elements |
29
+ | `className` | `string` | — | Additional CSS class |
30
+
31
+ ## Visual reference
32
+
33
+ | State | Border | Availability color |
34
+ |---|---|---|
35
+ | Unselected | 1px subtle gray (`--color-border-subtle`) | Primary (#000) |
36
+ | Selected | 2px accent blue (`--color-border-accent`) | Success green (`--color-type-success`) |
37
+
38
+ ## Sizing
39
+
40
+ - Tile min-height: 124px
41
+ - Padding: 16px (`--spacing-utk-l`)
42
+ - Border radius: 0px (Gap square-corner identity)
43
+ - Title: Gap Sans, 16px, weight 400 (`--text-selector-header-tile-font-weight`)
44
+ - Subtext / availability: Gap Sans, 16px, weight 400
45
+ - Gap between tiles: 8px (`--spacing-utk-s`)
46
+ - Tiles are equal-width (flex: 1 1 0)
47
+
48
+ ## Examples
49
+
50
+ ```tsx
51
+ {/* Basic shipping vs. pickup */}
52
+ <Fulfillment>
53
+ <FulfillmentTile
54
+ title="Free shipping"
55
+ selected={method === "ship"}
56
+ availability="In Stock"
57
+ onClick={() => setMethod("ship")}
58
+ >
59
+ <span>on $50+ for Rewards Members. </span>
60
+ <a href="/rewards">Sign in</a>
61
+ </FulfillmentTile>
62
+ <FulfillmentTile
63
+ title="In-store pickup"
64
+ selected={method === "pickup"}
65
+ onClick={() => setMethod("pickup")}
66
+ >
67
+ <a href="/store-locator">Find a store</a>
68
+ </FulfillmentTile>
69
+ </Fulfillment>
70
+
71
+ {/* With store selected */}
72
+ <Fulfillment>
73
+ <FulfillmentTile
74
+ title="Free shipping"
75
+ availability="In Stock"
76
+ onClick={() => setMethod("ship")}
77
+ >
78
+ on $50+ for Rewards Members.
79
+ </FulfillmentTile>
80
+ <FulfillmentTile
81
+ title="In-store pickup"
82
+ selected
83
+ availability="In Stock"
84
+ onClick={() => setMethod("pickup")}
85
+ >
86
+ <span>Walnut Creek</span>
87
+ <br />
88
+ <a href="/change-store">Change store</a>
89
+ </FulfillmentTile>
90
+ </Fulfillment>
91
+ ```
92
+
93
+ ## Rules
94
+
95
+ - Always provide two tiles: shipping and pickup — they sit side by side
96
+ - Use `selected` to highlight the active fulfillment method
97
+ - When selected, the "In Stock" availability text turns green (`--color-type-success`)
98
+ - Place links (e.g. "Sign in", "Find a store") inside `children`
99
+ - The component is a `<button>` for keyboard accessibility
100
+ - Each tile flexes to fill available width equally
@@ -0,0 +1,71 @@
1
+ # Global Footer
2
+
3
+ The site-wide footer containing promotional blocks (email sign-up, rewards credit card, app download), navigation columns, social links, and legal text.
4
+
5
+ ## Usage
6
+
7
+ ```jsx
8
+ import { GlobalFooter } from "@stoovles/gap-kit";
9
+
10
+ <GlobalFooter />
11
+ ```
12
+
13
+ All sections ship with Gap-branded defaults, so a bare `<GlobalFooter />` renders a fully populated footer.
14
+
15
+ ## Props
16
+
17
+ | Prop | Type | Default | Description |
18
+ |------|------|---------|-------------|
19
+ | `blocks` | `FooterBlock[]` | Email, Credit Card, App | Top-row promotional blocks |
20
+ | `navColumns` | `FooterNavColumn[]` | Customer Support, Rewards, About Us, Find Us | Navigation link columns |
21
+ | `socialLinks` | `SocialLink[]` | TikTok, Instagram, YouTube, Spotify | Social icons below the last nav column |
22
+ | `legalLines` | `(string \| ReactNode)[]` | Gap Inc. legal boilerplate | Bottom legal text lines |
23
+
24
+ ### FooterBlock
25
+
26
+ | Field | Type | Description |
27
+ |-------|------|-------------|
28
+ | `title` | `string` | Block heading |
29
+ | `content` | `ReactNode` | Free-form block body (email form, links, copy, etc.) |
30
+
31
+ ### FooterNavColumn
32
+
33
+ | Field | Type | Description |
34
+ |-------|------|-------------|
35
+ | `title` | `string` | Column heading |
36
+ | `links` | `{ label, href }[]` | List of footer links |
37
+
38
+ ### SocialLink
39
+
40
+ | Field | Type | Description |
41
+ |-------|------|-------------|
42
+ | `label` | `string` | Accessible name |
43
+ | `href` | `string` | Link URL |
44
+ | `icon` | `ReactNode` | 32×32 SVG icon |
45
+
46
+ ## Visual reference
47
+
48
+ - **Background:** White (`--global-footer-background`)
49
+ - **Top blocks:** 3-column layout with border-right dividers, 18px bold titles
50
+ - **Nav columns:** 4-column row, 16px bold headers, 16px secondary gray links
51
+ - **Social icons:** 32×32px dark circular icons (TikTok, Instagram, YouTube, Spotify)
52
+ - **Legal text:** 12px body, pipe-separated items across two lines
53
+
54
+ ## Examples
55
+
56
+ Override just the nav columns:
57
+
58
+ ```jsx
59
+ <GlobalFooter
60
+ navColumns={[
61
+ { title: "Help", links: [{ label: "FAQ", href: "/faq" }] },
62
+ { title: "About", links: [{ label: "Careers", href: "/careers" }] },
63
+ ]}
64
+ />
65
+ ```
66
+
67
+ ## Rules
68
+
69
+ - Always render at the bottom of every page layout.
70
+ - The footer is full-width; inner content is constrained to 1264px.
71
+ - Do not remove the legal section in production prototypes.
@@ -0,0 +1,67 @@
1
+ # Global Header
2
+
3
+ The top-level navigation bar present on every page. Contains the sister-brand bar, a promotional banner (EDFS — Everyday Free Shipping), and account/bag action icons.
4
+
5
+ ## Usage
6
+
7
+ ```jsx
8
+ import { GlobalHeader } from "@stoovles/gap-kit";
9
+
10
+ <GlobalHeader
11
+ promoText="Free shipping on $50+ for Rewards Members"
12
+ promoLinks={[
13
+ { label: "Sign In or Join", href: "/signin" },
14
+ { label: "Details", href: "/shipping-details" },
15
+ ]}
16
+ bagCount={3}
17
+ onBagClick={() => openBag()}
18
+ onAccountClick={() => openAccount()}
19
+ />
20
+ ```
21
+
22
+ ## Props
23
+
24
+ | Prop | Type | Default | Description |
25
+ |------|------|---------|-------------|
26
+ | `brands` | `BrandLink[]` | Gap, Gap Factory, Old Navy, Banana Republic, Athleta, Banana Republic Factory | Sister-brand links in the top-left bar |
27
+ | `promoText` | `string` | `"Free shipping on $50+ for Rewards Members"` | Promotional text in center |
28
+ | `promoLinks` | `{ label, href }[]` | Sign In or Join, Details | Underlined links after promo text |
29
+ | `bagCount` | `number` | — | Items in the shopping bag; shows a badge when > 0 |
30
+ | `onBagClick` | `() => void` | — | Callback for bag icon click |
31
+ | `onAccountClick` | `() => void` | — | Callback for account icon click |
32
+ | `onLogoClick` | `() => void` | — | Callback for logo click |
33
+
34
+ ## Visual reference
35
+
36
+ - **Background:** Solid black (`--global-header-brand-bar-bg`)
37
+ - **Brand links:** Gray text at 10px, white on hover
38
+ - **Promo area:** White text centered, underlined links
39
+ - **Icons:** White 24px person + 18×24px bag outlines
40
+ - **Badge:** 16×16px white circle with black count text, positioned inside the bag icon
41
+ - **Height:** 48px fixed
42
+
43
+ ## Examples
44
+
45
+ Minimal header with no bag items:
46
+
47
+ ```jsx
48
+ <GlobalHeader bagCount={0} />
49
+ ```
50
+
51
+ Custom brands:
52
+
53
+ ```jsx
54
+ <GlobalHeader
55
+ brands={[
56
+ { label: "Gap", href: "/gap" },
57
+ { label: "Old Navy", href: "/oldnavy" },
58
+ ]}
59
+ />
60
+ ```
61
+
62
+ ## Rules
63
+
64
+ - Always render the header at the very top of every page layout.
65
+ - The sister-brand bar scrolls horizontally if the viewport is too narrow.
66
+ - The promo text should reflect the current promotion; override via `promoText`.
67
+ - Do not put the header inside another container that constrains its width.
@@ -0,0 +1,79 @@
1
+ # Hamburger Navigation
2
+
3
+ A mobile-style slide-out navigation drawer that covers the viewport with a dark overlay. Contains the sister-brand bar, a search field, department links with right chevrons, and secondary utility links on a subtle gray background.
4
+
5
+ ## Usage
6
+
7
+ ```jsx
8
+ import { HamburgerNav } from "@stoovles/gap-kit";
9
+
10
+ const [navOpen, setNavOpen] = useState(false);
11
+
12
+ <HamburgerNav
13
+ open={navOpen}
14
+ onClose={() => setNavOpen(false)}
15
+ sections={[
16
+ {
17
+ items: [
18
+ { label: "New", hasChildren: true, onClick: () => {} },
19
+ { label: "Women", hasChildren: true, onClick: () => {} },
20
+ { label: "Men", hasChildren: true, onClick: () => {} },
21
+ { label: "Sale", hasChildren: true, onClick: () => {} },
22
+ ],
23
+ },
24
+ {
25
+ secondary: true,
26
+ items: [
27
+ { label: "Customer Service" },
28
+ { label: "Orders & Returns" },
29
+ { label: "Gift Cards", hasChildren: true },
30
+ { label: "Find a Store" },
31
+ { label: "Sign In" },
32
+ ],
33
+ },
34
+ ]}
35
+ onSearch={(q) => navigate(`/search?q=${q}`)}
36
+ />
37
+ ```
38
+
39
+ ## Props
40
+
41
+ | Prop | Type | Default | Description |
42
+ |------|------|---------|-------------|
43
+ | `open` | `boolean` | **required** | Whether the drawer is visible |
44
+ | `onClose` | `() => void` | **required** | Called to close the drawer |
45
+ | `sections` | `HamburgerNavSection[]` | **required** | Nav sections |
46
+ | `headerSlot` | `ReactNode` | — | Content above the search (e.g. brand bar, promo) |
47
+ | `searchPlaceholder` | `string` | `"Search Gap"` | Search input placeholder |
48
+ | `onSearch` | `(value: string) => void` | — | Fires on Enter in search |
49
+
50
+ ### HamburgerNavSection
51
+
52
+ | Field | Type | Description |
53
+ |-------|------|-------------|
54
+ | `items` | `HamburgerNavItem[]` | Nav items in this section |
55
+ | `secondary` | `boolean` | Gray background for utility links |
56
+
57
+ ### HamburgerNavItem
58
+
59
+ | Field | Type | Description |
60
+ |-------|------|-------------|
61
+ | `label` | `string` | Display text |
62
+ | `href` | `string` | Optional link URL (renders `<a>` vs `<button>`) |
63
+ | `hasChildren` | `boolean` | Shows a right chevron |
64
+ | `onClick` | `() => void` | Click callback |
65
+
66
+ ## Visual reference
67
+
68
+ - **Overlay:** 30% dark transparent background covering the full viewport
69
+ - **Panel:** 350px wide, white, slides in from the left
70
+ - **Search:** 44px bordered input with magnifying glass icon
71
+ - **Nav rows:** 16px text, 24px top padding per row, right chevron for expandable items
72
+ - **Secondary section:** subtle gray background (#f7f7f7)
73
+ - **Close button:** White X icon positioned to the right of the panel
74
+
75
+ ## Rules
76
+
77
+ - The hamburger nav is a fixed-position overlay — it blocks all content behind it.
78
+ - Always provide `onClose`; the overlay click and X button both trigger it.
79
+ - Split navigation into primary (departments) and secondary (utility) sections.
@@ -0,0 +1,49 @@
1
+ # Handle
2
+
3
+ ## When to use
4
+
5
+ Use the `Handle` component as a draggable thumb for sliders, range selectors, and similar interactive controls. This is a visual primitive — pair it with your own drag logic.
6
+
7
+ ```tsx
8
+ import { Handle } from '@stoovles/gap-kit'
9
+ ```
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Description |
14
+ |---|---|---|---|
15
+ | `size` | `"large" \| "small"` | `"large"` | Large is 24px, small is 20px |
16
+ | `className` | `string` | — | Additional CSS class |
17
+
18
+ ## Visual reference
19
+
20
+ | State | Ring | Thumb |
21
+ |---|---|---|
22
+ | Default | 32px invisible ring | White circle with drop shadow |
23
+ | Hover | 48px, gray (#757575) at 30% opacity | White circle with drop shadow |
24
+ | Press | 48px, gray (#757575) at 50% opacity | White circle with drop shadow |
25
+
26
+ ## Sizing
27
+
28
+ | Size | Thumb diameter | Default ring | Hover/press ring |
29
+ |---|---|---|---|
30
+ | `large` | 24px | 32px | 48px |
31
+ | `small` | 20px | 32px | 48px |
32
+
33
+ ## Examples
34
+
35
+ ```tsx
36
+ {/* Default large handle */}
37
+ <Handle />
38
+
39
+ {/* Small handle for compact sliders */}
40
+ <Handle size="small" />
41
+ ```
42
+
43
+ ## Rules
44
+
45
+ - Always use within a slider or range control context
46
+ - The handle uses `cursor: grab` by default and `cursor: grabbing` when active
47
+ - The hover ring provides a generous touch target — do not override its size
48
+ - Background is always white; the drop shadow provides visual lift
49
+ - Do not add borders or outlines to the handle — it relies on shadow for definition
@@ -0,0 +1,147 @@
1
+ # Icon
2
+
3
+ ## When to use
4
+
5
+ Use the `Icon` component for all iconography. Never use raw SVGs, emoji, or Unicode symbols. Always import from `@stoovles/gap-kit`.
6
+
7
+ ```tsx
8
+ import { Icon } from '@stoovles/gap-kit'
9
+ ```
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Description |
14
+ |---|---|---|---|
15
+ | `name` | `IconName` | required | Icon identifier (see catalog below) |
16
+ | `size` | `12 \| 16 \| 24 \| 32` | `16` | Pixel size of the icon |
17
+ | `color` | `"dark" \| "light"` | `"dark"` | Dark renders in `--color-icon-default` (#000), Light renders in `--color-icon-inverse` (#fff) |
18
+ | `className` | `string` | — | Additional CSS class |
19
+ | `aria-label` | `string` | — | Accessible label; when set, icon has `role="img"` |
20
+
21
+ ## Icon catalog
22
+
23
+ ### Navigation
24
+
25
+ | Name | Purpose | Common sizes |
26
+ |---|---|---|
27
+ | `chevron-left` | Navigate back, collapse | 12, 16 |
28
+ | `chevron-right` | Navigate forward, expand | 12, 16 |
29
+ | `chevron-down` | Expand dropdown, accordion | 12, 16 |
30
+ | `chevron-up` | Collapse dropdown, accordion | 12, 16 |
31
+ | `menu` | Hamburger menu toggle | 16, 24 |
32
+ | `x` | Close, dismiss, remove | 12, 16, 24 |
33
+
34
+ ### Actions
35
+
36
+ | Name | Purpose | Common sizes |
37
+ |---|---|---|
38
+ | `search` | Search trigger or input icon | 16, 24 |
39
+ | `filter` | Filter controls toggle | 16 |
40
+ | `bag` | Shopping bag | 16, 24 |
41
+ | `profile` | User account | 16, 24 |
42
+ | `quickadd` | Add to bag (bag + plus) | 16, 24 |
43
+
44
+ ### Status & feedback
45
+
46
+ | Name | Purpose | Common sizes |
47
+ |---|---|---|
48
+ | `checkmark-filled` | Confirmed, completed (solid circle) | 12, 16 |
49
+ | `checkmark-outlined` | Selected, checked (stroke only) | 12, 16 |
50
+ | `alert-circle` | Warning or error (solid circle) | 16 |
51
+ | `info-circle` | Informational (solid circle) | 16 |
52
+ | `info` | Informational (outlined circle) | 12, 16, 24 |
53
+ | `loader` | Loading animation dots | 16, 24 |
54
+
55
+ ### Media
56
+
57
+ | Name | Purpose | Common sizes |
58
+ |---|---|---|
59
+ | `play` | Play video/audio | 12, 16 |
60
+ | `pause` | Pause video/audio | 12, 16 |
61
+ | `volume-on` | Audio on/unmuted | 16 |
62
+ | `volume-off` | Audio off/muted | 16 |
63
+ | `star-filled` | Full rating star | 16 |
64
+ | `star-half` | Half rating star | 16 |
65
+ | `star-empty` | Empty rating star | 16 |
66
+
67
+ ### Content
68
+
69
+ | Name | Purpose | Common sizes |
70
+ |---|---|---|
71
+ | `fire` | Trending, hot | 16 |
72
+ | `show` | Eye/visibility toggle | 16 |
73
+ | `email-outlined` | Email, contact | 16 |
74
+ | `location-outlined` | Store locator (outlined) | 16 |
75
+ | `location-filled` | Current location (solid) | 16 |
76
+ | `phone` | Phone, call | 16 |
77
+ | `swatch` | Color swatch circle | 16 |
78
+
79
+ ## Size decision tree
80
+
81
+ ```
82
+ Which icon size should I use?
83
+
84
+ ├─ Inline with small text (captions, badges)?
85
+ │ └─ 12
86
+
87
+ ├─ Standard UI element (buttons, inputs, cards)?
88
+ │ └─ 16 (default)
89
+
90
+ ├─ Prominent action (header icons, primary actions)?
91
+ │ └─ 24
92
+
93
+ └─ Social media badge or large display?
94
+ └─ 32
95
+ ```
96
+
97
+ ## Color decision tree
98
+
99
+ ```
100
+ Which icon color should I use?
101
+
102
+ ├─ On a light background (white, gray, brand-tertiary)?
103
+ │ └─ color="dark" (default)
104
+
105
+ └─ On a dark background (black, brand fill, dark overlay)?
106
+ └─ color="light"
107
+ ```
108
+
109
+ ## Examples
110
+
111
+ ```tsx
112
+ {/* Standard navigation chevron */}
113
+ <Icon name="chevron-right" size={16} />
114
+
115
+ {/* Search icon in header (on dark background) */}
116
+ <Icon name="search" size={24} color="light" />
117
+
118
+ {/* Rating stars */}
119
+ <div style={{ display: 'flex', gap: '2px' }}>
120
+ <Icon name="star-filled" size={16} />
121
+ <Icon name="star-filled" size={16} />
122
+ <Icon name="star-filled" size={16} />
123
+ <Icon name="star-half" size={16} />
124
+ <Icon name="star-empty" size={16} />
125
+ </div>
126
+
127
+ {/* Close button with accessible label */}
128
+ <button>
129
+ <Icon name="x" size={24} aria-label="Close dialog" />
130
+ </button>
131
+
132
+ {/* Bag icon with count */}
133
+ <div style={{ position: 'relative' }}>
134
+ <Icon name="bag" size={24} color="light" />
135
+ <span className="bag-count">3</span>
136
+ </div>
137
+ ```
138
+
139
+ ## Rules
140
+
141
+ - Always use `name` prop with a valid icon name from the catalog — do not guess names
142
+ - Default size is 16 — only change when a larger or smaller icon is specifically needed
143
+ - Use `color="light"` only on dark backgrounds — never on white or light gray surfaces
144
+ - For clickable icons, wrap in a `<button>` element and add `aria-label` to the Icon
145
+ - Icons inherit the container's alignment — use flexbox to center if needed
146
+ - Do not apply custom fill or stroke colors via className — use the `color` prop
147
+ - Star rating always uses exactly 5 icons: a combination of `star-filled`, `star-half`, and `star-empty`
@@ -0,0 +1,70 @@
1
+ # Link
2
+
3
+ ## When to use
4
+
5
+ Use the `Link` component for navigational text that routes to another page or section. Renders a semantic `<a>` element.
6
+
7
+ ```tsx
8
+ import { Link } from '@stoovles/gap-kit'
9
+ ```
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Description |
14
+ |---|---|---|---|
15
+ | `variant` | `"inline" \| "subtle"` | `"inline"` | Inline is underlined and blue; subtle is plain gray |
16
+ | `children` | `ReactNode` | — | Link label text |
17
+ | `href` | `string` | — | Destination URL |
18
+ | `className` | `string` | — | Additional CSS class |
19
+
20
+ All standard `<a>` HTML attributes are also supported (e.g. `target`, `rel`, `aria-label`).
21
+
22
+ ## Variant decision tree
23
+
24
+ ```
25
+ What context is this link in?
26
+
27
+ ├─ Within body copy, or a primary navigational action?
28
+ │ └─ variant="inline" (underlined blue)
29
+
30
+ └─ Secondary or de-emphasized link (footer, breadcrumb, supporting text)?
31
+ └─ variant="subtle" (plain gray, underlines on hover)
32
+ ```
33
+
34
+ ## Visual reference
35
+
36
+ | Variant | Default | Hover / Active | Visited |
37
+ |---|---|---|---|
38
+ | `inline` | Blue text, blue 1px underline | Blue text, blue underline | Gray text (#757575), gray underline |
39
+ | `subtle` | Gray text (#757575), no underline | Blue text, blue 1px underline | Gray text, no underline |
40
+
41
+ ## Sizing
42
+
43
+ - Font: Gap Sans, 16px, weight 400, letter-spacing 0.32px
44
+ - Underline: 1px solid bottom border
45
+ - Focus ring: 2px blue (#031ba1) outline, 2px offset
46
+
47
+ ## Examples
48
+
49
+ ```tsx
50
+ {/* Inline link in body text */}
51
+ <p>
52
+ Check our <Link href="/returns">return policy</Link> for details.
53
+ </p>
54
+
55
+ {/* Subtle link in footer */}
56
+ <Link variant="subtle" href="/privacy">Privacy Policy</Link>
57
+
58
+ {/* External link */}
59
+ <Link href="https://gap.com" target="_blank" rel="noopener noreferrer">
60
+ Gap.com
61
+ </Link>
62
+ ```
63
+
64
+ ## Rules
65
+
66
+ - Always use `inline` for links within body copy so they are clearly identifiable
67
+ - Use `subtle` only where context already implies navigation (e.g. footer link lists)
68
+ - Never use a `Link` for actions — use `Button` instead
69
+ - Always provide an `href` — for click-only actions without navigation, use `Button`
70
+ - For external links, include `target="_blank"` and `rel="noopener noreferrer"`