diff --git a/accordion/README.md b/accordion/README.md new file mode 100644 index 0000000..fc72160 --- /dev/null +++ b/accordion/README.md @@ -0,0 +1,92 @@ +# Accordion +A fully accessible accordion component that displays collapsible content sections with smooth slide animations. + +## Getting Started + +Install dependencies: +```bash +npm install +``` + +Share the component to your Webflow workspace: +```bash +npx webflow library share +``` + +For local development: +```bash +npm run dev +``` + +## Designer Properties + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| ID | Id | — | HTML ID attribute for targeting and accessibility | +| Variant | Variant | bordered | Visual style of the accordion (bordered or flush) | +| Expansion Mode | Variant | single | Controls whether multiple sections can be open at once (single or multiple) | +| Item 1 Visible | Visibility | — | Show or hide the first accordion item | +| Item 1 Title | Text | What is your return policy? | Header title for the first accordion item | +| Item 1 Content | RichText | We offer a 30-day money-back guarantee... | Body content for the first accordion item | +| Item 2 Visible | Visibility | — | Show or hide the second accordion item | +| Item 2 Title | Text | How long does shipping take? | Header title for the second accordion item | +| Item 2 Content | RichText | Standard shipping typically takes... | Body content for the second accordion item | +| Item 3 Visible | Visibility | — | Show or hide the third accordion item | +| Item 3 Title | Text | Do you offer international shipping? | Header title for the third accordion item | +| Item 3 Content | RichText | Yes, we ship to over 50 countries... | Body content for the third accordion item | +| Item 4 Visible | Visibility | — | Show or hide the fourth accordion item | +| Item 4 Title | Text | How can I track my order? | Header title for the fourth accordion item | +| Item 4 Content | RichText | Once your order ships, you'll receive... | Body content for the fourth accordion item | +| Item 5 Visible | Visibility | — | Show or hide the fifth accordion item | +| Item 5 Title | Text | What payment methods do you accept? | Header title for the fifth accordion item | +| Item 5 Content | RichText | We accept all major credit cards... | Body content for the fifth accordion item | + +## Styling + +This component automatically adapts to your Webflow site's design system through site variables and inherited properties. + +### Site Variables + +To match your site's design system, define these CSS variables in your Webflow project settings. The component will use the fallback values shown below until you configure them. + +| Site Variable | What It Controls | Fallback | +|---------------|------------------|----------| +| --background-primary | Accordion item background color | #ffffff | +| --background-secondary | Button hover state background | #f5f5f5 | +| --text-primary | Title and content text color | #1a1a1a | +| --text-secondary | Muted text elements color | — | +| --border-color | Item borders and dividers | #e5e5e5 | +| --accent-color | Focus outline and link color | #1a1a1a | +| --border-radius | Corner rounding for bordered variant | 8px | + +### Inherited Properties + +The component inherits these CSS properties from its parent element: +- `font-family` — Typography style +- `color` — Text color +- `line-height` — Text spacing + +## Extending in Code + +### Custom Animation Duration +Modify the slide animation speed by targeting the content wrapper: +```css +.wf-accordion-content { + transition: max-height 0.5s ease-in-out; +} +``` + +### Adjust Item Spacing +Control the padding around accordion items: +```css +.wf-accordion-button { + padding: 20px 24px; +} +.wf-accordion-body { + padding: 0 24px 24px 24px; +} +``` + +## Dependencies + +No external dependencies. \ No newline at end of file diff --git a/accordion/index.html b/accordion/index.html new file mode 100644 index 0000000..ae9054f --- /dev/null +++ b/accordion/index.html @@ -0,0 +1,17 @@ + + + + + + Accordion + + + +
+ + + diff --git a/accordion/metadata.json b/accordion/metadata.json new file mode 100644 index 0000000..8ac3bc9 --- /dev/null +++ b/accordion/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Accordion", + "description": "Collapsible content sections with smooth animations. Supports single and multiple open modes, bordered and flush variants.", + "category": "Layout" +} diff --git a/accordion/package.json b/accordion/package.json new file mode 100644 index 0000000..f4f7574 --- /dev/null +++ b/accordion/package.json @@ -0,0 +1,25 @@ +{ + "name": "accordion", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.1.1", + "react-dom": "^19.1.1" + }, + "devDependencies": { + "@types/react": "^19.1.13", + "@types/react-dom": "^19.1.9", + "@vitejs/plugin-react": "^5.0.3", + "@webflow/data-types": "^1.0.1", + "@webflow/react": "^1.0.1", + "@webflow/webflow-cli": "^1.8.44", + "typescript": "~5.8.3", + "vite": "^7.1.7" + } +} \ No newline at end of file diff --git a/accordion/screenshot-brand.png b/accordion/screenshot-brand.png new file mode 100644 index 0000000..f11a7d4 Binary files /dev/null and b/accordion/screenshot-brand.png differ diff --git a/accordion/screenshot-dark.png b/accordion/screenshot-dark.png new file mode 100644 index 0000000..ffe5e5e Binary files /dev/null and b/accordion/screenshot-dark.png differ diff --git a/accordion/screenshot-light.png b/accordion/screenshot-light.png new file mode 100644 index 0000000..ffe5e5e Binary files /dev/null and b/accordion/screenshot-light.png differ diff --git a/accordion/src/components/Accordion/Accordion.css b/accordion/src/components/Accordion/Accordion.css new file mode 100644 index 0000000..1111b7c --- /dev/null +++ b/accordion/src/components/Accordion/Accordion.css @@ -0,0 +1,159 @@ +/* + * Webflow Site Variables Used: + * - --background-primary: Accordion background + * - --background-secondary: Button hover state + * - --text-primary: Title and content text + * - --text-secondary: Muted text elements + * - --border-color: Item borders and dividers + * - --accent-color: Focus outline color + * - --border-radius: Corner rounding for bordered variant + */ + +/* Box sizing reset */ +.wf-accordion *, +.wf-accordion *::before, +.wf-accordion *::after { + box-sizing: border-box; +} + +/* Root element - inherit Webflow typography + default padding */ +.wf-accordion { + font-family: inherit; + color: inherit; + line-height: inherit; + padding: 24px; +} + +/* Bordered variant container */ +.wf-accordion-bordered { + border: 1px solid var(--border-color, #e5e5e5); + border-radius: var(--border-radius, 8px); + overflow: hidden; +} + +/* Flush variant container */ +.wf-accordion-flush { + border: none; +} + +/* Accordion item */ +.wf-accordion-item { + background: var(--background-primary, #ffffff); +} + +/* Bordered variant item borders */ +.wf-accordion-bordered .wf-accordion-item + .wf-accordion-item { + border-top: 1px solid var(--border-color, #e5e5e5); +} + +/* Flush variant item borders */ +.wf-accordion-flush .wf-accordion-item + .wf-accordion-item { + border-top: 1px solid var(--border-color, #e5e5e5); +} + +/* Header wrapper */ +.wf-accordion-header { + margin: 0; + padding: 0; +} + +/* Accordion button */ +.wf-accordion-button { + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding: 16px 20px; + background: transparent; + border: none; + cursor: pointer; + text-align: left; + color: var(--text-primary, #1a1a1a); + font-size: 16px; + font-weight: 500; + transition: background-color 0.2s; +} + +.wf-accordion-button:hover { + background: var(--background-secondary, #f5f5f5); +} + +.wf-accordion-button:focus-visible { + outline: 2px solid var(--accent-color, #1a1a1a); + outline-offset: -2px; + z-index: 1; + position: relative; +} + +.wf-accordion-button:active { + background: var(--background-secondary, #f5f5f5); +} + +/* Title text */ +.wf-accordion-title { + flex: 1; + min-width: 0; +} + +/* Chevron icon */ +.wf-accordion-icon { + flex-shrink: 0; + transition: transform 0.3s ease; + color: var(--text-primary, #1a1a1a); +} + +.wf-accordion-icon-expanded { + transform: rotate(180deg); +} + +/* Content wrapper with slide animation */ +.wf-accordion-content { + max-height: 0; + overflow: hidden; + transition: max-height 0.3s ease; +} + +/* Content body */ +.wf-accordion-body { + padding: 0 20px 20px 20px; + color: var(--text-primary, #1a1a1a); + font-size: 15px; + line-height: 1.6; +} + +/* Rich text content styling */ +.wf-accordion-body p { + margin: 0 0 12px 0; +} + +.wf-accordion-body p:last-child { + margin-bottom: 0; +} + +.wf-accordion-body a { + color: var(--accent-color, #1a1a1a); + text-decoration: underline; +} + +.wf-accordion-body a:hover { + text-decoration: none; +} + +.wf-accordion-body ul, +.wf-accordion-body ol { + margin: 0 0 12px 0; + padding-left: 24px; +} + +.wf-accordion-body li { + margin-bottom: 4px; +} + +.wf-accordion-body strong { + font-weight: 600; +} + +.wf-accordion-body em { + font-style: italic; +} \ No newline at end of file diff --git a/accordion/src/components/Accordion/Accordion.tsx b/accordion/src/components/Accordion/Accordion.tsx new file mode 100644 index 0000000..ade90a8 --- /dev/null +++ b/accordion/src/components/Accordion/Accordion.tsx @@ -0,0 +1,161 @@ +import { useState, useRef, useEffect } from "react"; + +export interface AccordionProps { + id?: string; + variant?: "bordered" | "flush"; + expansionMode?: "single" | "multiple"; + item1Visible?: boolean; + item1Title?: string; + item1Content?: string; + item2Visible?: boolean; + item2Title?: string; + item2Content?: string; + item3Visible?: boolean; + item3Title?: string; + item3Content?: string; + item4Visible?: boolean; + item4Title?: string; + item4Content?: string; + item5Visible?: boolean; + item5Title?: string; + item5Content?: string; +} + +interface AccordionItem { + id: string; + title: string; + content: string; + visible: boolean; +} + +export default function Accordion({ + id, + variant = "bordered", + expansionMode = "single", + item1Visible = true, + item1Title = "What is your return policy?", + item1Content = "We offer a 30-day money-back guarantee on all purchases. If you're not completely satisfied with your order, you can return it for a full refund within 30 days of delivery.", + item2Visible = true, + item2Title = "How long does shipping take?", + item2Content = "Standard shipping typically takes 5-7 business days. Express shipping options are available at checkout for delivery within 2-3 business days.", + item3Visible = true, + item3Title = "Do you offer international shipping?", + item3Content = "Yes, we ship to over 50 countries worldwide. International shipping rates and delivery times vary by destination and will be calculated at checkout.", + item4Visible = true, + item4Title = "How can I track my order?", + item4Content = "Once your order ships, you'll receive a tracking number via email. You can use this number to monitor your shipment's progress on our website or the carrier's tracking page.", + item5Visible = true, + item5Title = "What payment methods do you accept?", + item5Content = "We accept all major credit cards (Visa, MasterCard, American Express, Discover), PayPal, Apple Pay, and Google Pay for your convenience.", +}: AccordionProps) { + const items: AccordionItem[] = [ + { id: "item-1", title: item1Title, content: item1Content, visible: item1Visible }, + { id: "item-2", title: item2Title, content: item2Content, visible: item2Visible }, + { id: "item-3", title: item3Title, content: item3Content, visible: item3Visible }, + { id: "item-4", title: item4Title, content: item4Content, visible: item4Visible }, + { id: "item-5", title: item5Title, content: item5Content, visible: item5Visible }, + ].filter((item) => item.visible); + + const [expandedItems, setExpandedItems] = useState>( + new Set(items.length > 0 ? [items[0].id] : []) + ); + const contentRefs = useRef>(new Map()); + + const toggleItem = (itemId: string) => { + setExpandedItems((prev) => { + const newSet = new Set(prev); + if (newSet.has(itemId)) { + newSet.delete(itemId); + } else { + if (expansionMode === "single") { + newSet.clear(); + } + newSet.add(itemId); + } + return newSet; + }); + }; + + const handleKeyDown = (event: React.KeyboardEvent, itemId: string) => { + if (event.key === "Enter" || event.key === " ") { + event.preventDefault(); + toggleItem(itemId); + } + }; + + useEffect(() => { + contentRefs.current.forEach((element, itemId) => { + if (element) { + if (expandedItems.has(itemId)) { + element.style.maxHeight = `${element.scrollHeight}px`; + } else { + element.style.maxHeight = "0px"; + } + } + }); + }, [expandedItems]); + + return ( +
+ {items.map((item) => { + const isExpanded = expandedItems.has(item.id); + const buttonId = `wf-accordion-button-${item.id}`; + const panelId = `wf-accordion-panel-${item.id}`; + + return ( +
+

+ +

+
{ + if (el) { + contentRefs.current.set(item.id, el); + } else { + contentRefs.current.delete(item.id); + } + }} + className="wf-accordion-content" + role="region" + aria-labelledby={buttonId} + aria-hidden={!isExpanded} + > +
+
+
+ ); + })} +
+ ); +} \ No newline at end of file diff --git a/accordion/src/components/Accordion/Accordion.webflow.tsx b/accordion/src/components/Accordion/Accordion.webflow.tsx new file mode 100644 index 0000000..e271c41 --- /dev/null +++ b/accordion/src/components/Accordion/Accordion.webflow.tsx @@ -0,0 +1,115 @@ +import Accordion from "./Accordion"; +import { props } from "@webflow/data-types"; +import { declareComponent } from "@webflow/react"; +import "./Accordion.css"; + +export default declareComponent(Accordion, { + name: "Accordion", + description: "A fully accessible accordion component that displays collapsible content sections with smooth slide animations. Each section consists of a clickable header with a title and an animated chevron icon that rotates 180 degrees when expanded. Supports two expansion modes: 'single' mode where opening one section automatically closes others, and 'multiple' mode where any number of sections can be open simultaneously. Features two visual variants: a 'bordered' style with visible borders around each section, and a 'flush' minimal style with subtle dividers. The first item is expanded by default on initial render. Fully keyboard accessible with Enter and Space key support, and implements proper ARIA attributes for screen readers. Responsive design ensures readability on all screen sizes.", + group: "Interactive", + options: { + ssr: false, + applyTagSelectors: true + }, + props: { + id: props.Id({ + name: "Element ID", + group: "Settings", + tooltip: "HTML ID attribute for targeting and accessibility" + }), + variant: props.Variant({ + name: "Variant", + options: ["bordered", "flush"], + defaultValue: "bordered", + group: "Style", + tooltip: "Visual style of the accordion" + }), + expansionMode: props.Variant({ + name: "Expansion Mode", + options: ["single", "multiple"], + defaultValue: "single", + group: "Behavior", + tooltip: "Controls whether multiple sections can be open at once" + }), + item1Visible: props.Visibility({ + name: "Item 1 Visible", + group: "Item 1", + tooltip: "Show or hide the first accordion item" + }), + item1Title: props.Text({ + name: "Item 1 Title", + defaultValue: "What is your return policy?", + group: "Item 1", + tooltip: "Header title for the first accordion item" + }), + item1Content: props.RichText({ + name: "Item 1 Content", + group: "Item 1", + tooltip: "Body content for the first accordion item" + }), + item2Visible: props.Visibility({ + name: "Item 2 Visible", + group: "Item 2", + tooltip: "Show or hide the second accordion item" + }), + item2Title: props.Text({ + name: "Item 2 Title", + defaultValue: "How long does shipping take?", + group: "Item 2", + tooltip: "Header title for the second accordion item" + }), + item2Content: props.RichText({ + name: "Item 2 Content", + group: "Item 2", + tooltip: "Body content for the second accordion item" + }), + item3Visible: props.Visibility({ + name: "Item 3 Visible", + group: "Item 3", + tooltip: "Show or hide the third accordion item" + }), + item3Title: props.Text({ + name: "Item 3 Title", + defaultValue: "Do you offer international shipping?", + group: "Item 3", + tooltip: "Header title for the third accordion item" + }), + item3Content: props.RichText({ + name: "Item 3 Content", + group: "Item 3", + tooltip: "Body content for the third accordion item" + }), + item4Visible: props.Visibility({ + name: "Item 4 Visible", + group: "Item 4", + tooltip: "Show or hide the fourth accordion item" + }), + item4Title: props.Text({ + name: "Item 4 Title", + defaultValue: "How can I track my order?", + group: "Item 4", + tooltip: "Header title for the fourth accordion item" + }), + item4Content: props.RichText({ + name: "Item 4 Content", + group: "Item 4", + tooltip: "Body content for the fourth accordion item" + }), + item5Visible: props.Visibility({ + name: "Item 5 Visible", + group: "Item 5", + tooltip: "Show or hide the fifth accordion item" + }), + item5Title: props.Text({ + name: "Item 5 Title", + defaultValue: "What payment methods do you accept?", + group: "Item 5", + tooltip: "Header title for the fifth accordion item" + }), + item5Content: props.RichText({ + name: "Item 5 Content", + group: "Item 5", + tooltip: "Body content for the fifth accordion item" + }), + }, +}); \ No newline at end of file diff --git a/accordion/src/components/Accordion/AccordionSimple.webflow.tsx b/accordion/src/components/Accordion/AccordionSimple.webflow.tsx new file mode 100644 index 0000000..59a0e7f --- /dev/null +++ b/accordion/src/components/Accordion/AccordionSimple.webflow.tsx @@ -0,0 +1,101 @@ +import Accordion from "./Accordion"; +import { props } from "@webflow/data-types"; +import { declareComponent } from "@webflow/react"; +import "./Accordion.css"; + +export default declareComponent(Accordion, { + name: "Accordion (Simple)", + description: "A fully accessible accordion component that displays collapsible content sections with smooth slide animations. Each section consists of a clickable header with a title and an animated chevron icon that rotates 180 degrees when expanded. Supports two expansion modes: 'single' mode where opening one section automatically closes others, and 'multiple' mode where any number of sections can be open simultaneously. Features two visual variants: a 'bordered' style with visible borders around each section, and a 'flush' minimal style with subtle dividers. The first item is expanded by default on initial render. Fully keyboard accessible with Enter and Space key support, and implements proper ARIA attributes for screen readers. Responsive design ensures readability on all screen sizes.", + group: "Interactive", + options: { + ssr: false, + applyTagSelectors: true + }, + props: { + id: props.Id({ + name: "Element ID", + group: "Settings", + tooltip: "HTML ID attribute for targeting and accessibility" + }), + item1Visible: props.Visibility({ + name: "Item 1 Visible", + group: "Item 1", + tooltip: "Show or hide the first accordion item" + }), + item1Title: props.Text({ + name: "Item 1 Title", + defaultValue: "What is your return policy?", + group: "Item 1", + tooltip: "Header title for the first accordion item" + }), + item1Content: props.RichText({ + name: "Item 1 Content", + group: "Item 1", + tooltip: "Body content for the first accordion item" + }), + item2Visible: props.Visibility({ + name: "Item 2 Visible", + group: "Item 2", + tooltip: "Show or hide the second accordion item" + }), + item2Title: props.Text({ + name: "Item 2 Title", + defaultValue: "How long does shipping take?", + group: "Item 2", + tooltip: "Header title for the second accordion item" + }), + item2Content: props.RichText({ + name: "Item 2 Content", + group: "Item 2", + tooltip: "Body content for the second accordion item" + }), + item3Visible: props.Visibility({ + name: "Item 3 Visible", + group: "Item 3", + tooltip: "Show or hide the third accordion item" + }), + item3Title: props.Text({ + name: "Item 3 Title", + defaultValue: "Do you offer international shipping?", + group: "Item 3", + tooltip: "Header title for the third accordion item" + }), + item3Content: props.RichText({ + name: "Item 3 Content", + group: "Item 3", + tooltip: "Body content for the third accordion item" + }), + item4Visible: props.Visibility({ + name: "Item 4 Visible", + group: "Item 4", + tooltip: "Show or hide the fourth accordion item" + }), + item4Title: props.Text({ + name: "Item 4 Title", + defaultValue: "How can I track my order?", + group: "Item 4", + tooltip: "Header title for the fourth accordion item" + }), + item4Content: props.RichText({ + name: "Item 4 Content", + group: "Item 4", + tooltip: "Body content for the fourth accordion item" + }), + item5Visible: props.Visibility({ + name: "Item 5 Visible", + group: "Item 5", + tooltip: "Show or hide the fifth accordion item" + }), + item5Title: props.Text({ + name: "Item 5 Title", + defaultValue: "What payment methods do you accept?", + group: "Item 5", + tooltip: "Header title for the fifth accordion item" + }), + item5Content: props.RichText({ + name: "Item 5 Content", + group: "Item 5", + tooltip: "Body content for the fifth accordion item" + }), + }, +}); \ No newline at end of file diff --git a/accordion/src/main.tsx b/accordion/src/main.tsx new file mode 100644 index 0000000..5f9f7f8 --- /dev/null +++ b/accordion/src/main.tsx @@ -0,0 +1,321 @@ +import { StrictMode, useState } from "react" +import { createRoot } from "react-dom/client" +import Accordion from "./components/Accordion/Accordion" +import "./components/Accordion/Accordion.css" + +type ThemeVars = { + "--background-primary": string + "--background-secondary": string + "--text-primary": string + "--text-secondary": string + "--border-color": string + "--accent-color": string + "--accent-text-color": string + "--border-radius": string +} + +const themes: Record = { + light: { + "--background-primary": "#ffffff", + "--background-secondary": "#f5f5f5", + "--text-primary": "#1a1a1a", + "--text-secondary": "#737373", + "--border-color": "#e5e5e5", + "--accent-color": "#2563eb", + "--accent-text-color": "#ffffff", + "--border-radius": "8px", + }, + dark: { + "--background-primary": "#0a0a0a", + "--background-secondary": "#1a1a1a", + "--text-primary": "#fafafa", + "--text-secondary": "#a3a3a3", + "--border-color": "#2a2a2a", + "--accent-color": "#3b82f6", + "--accent-text-color": "#ffffff", + "--border-radius": "8px", + }, + brand: { + "--background-primary": "#fef7f0", + "--background-secondary": "#fde8d0", + "--text-primary": "#1c1917", + "--text-secondary": "#78716c", + "--border-color": "#e7e5e4", + "--accent-color": "#ea580c", + "--accent-text-color": "#ffffff", + "--border-radius": "12px", + }, +} + +function App() { + const [activeTheme, setActiveTheme] = useState<"light" | "dark" | "brand" | "custom">("light") + const [customVars, setCustomVars] = useState(themes.light) + + const currentVars = activeTheme === "custom" ? customVars : themes[activeTheme] + + const handleThemeChange = (theme: "light" | "dark" | "brand" | "custom") => { + setActiveTheme(theme) + if (theme !== "custom") { + setCustomVars(themes[theme]) + } + } + + const handleCustomVarChange = (key: keyof ThemeVars, value: string) => { + setCustomVars((prev) => ({ ...prev, [key]: value })) + } + + const pageBackgroundColor = activeTheme === "dark" ? "#000000" : activeTheme === "brand" ? "#fef3e8" : "#f9fafb" + + return ( +
+
+
+

+ Accordion Component Preview +

+ +
+
+ + + + +
+ + {activeTheme === "custom" && ( +
+

+ Custom Theme Editor +

+
+ {Object.entries(customVars).map(([key, value]) => ( +
+ + handleCustomVarChange(key as keyof ThemeVars, e.target.value)} + style={{ + width: "100%", + padding: key === "--border-radius" ? "8px" : "4px", + border: `1px solid ${activeTheme === "dark" ? "#2a2a2a" : "#e5e5e5"}`, + borderRadius: "4px", + fontSize: "14px", + backgroundColor: activeTheme === "dark" ? "#0a0a0a" : "#ffffff", + color: activeTheme === "dark" ? "#fafafa" : "#1a1a1a", + cursor: "pointer" + }} + /> +
+ ))} +
+
+ )} +
+
+
+ +
+
+
+
+

+ Default Configuration +

+ +
+ +
+

+ Flush Variant with Multiple Expansion +

+ +
+ +
+

+ Minimal FAQ (3 Items) +

+ +
+
+
+
+
+ ) +} + +createRoot(document.getElementById("root")!).render( + + + +) \ No newline at end of file diff --git a/accordion/src/vite-env.d.ts b/accordion/src/vite-env.d.ts new file mode 100644 index 0000000..151aa68 --- /dev/null +++ b/accordion/src/vite-env.d.ts @@ -0,0 +1 @@ +/// \ No newline at end of file diff --git a/accordion/tsconfig.app.json b/accordion/tsconfig.app.json new file mode 100644 index 0000000..d775f2a --- /dev/null +++ b/accordion/tsconfig.app.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsBuildInfo", + "target": "ES2022", + "useDefineForClassFields": true, + "lib": [ + "ES2022", + "DOM", + "DOM.Iterable" + ], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": [ + "src" + ] +} \ No newline at end of file diff --git a/accordion/tsconfig.json b/accordion/tsconfig.json new file mode 100644 index 0000000..65f670c --- /dev/null +++ b/accordion/tsconfig.json @@ -0,0 +1,11 @@ +{ + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.node.json" + } + ] +} \ No newline at end of file diff --git a/accordion/tsconfig.node.json b/accordion/tsconfig.node.json new file mode 100644 index 0000000..c4a9a48 --- /dev/null +++ b/accordion/tsconfig.node.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsBuildInfo", + "target": "ES2023", + "lib": [ + "ES2023" + ], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": [ + "vite.config.ts" + ] +} \ No newline at end of file diff --git a/accordion/vite.config.ts b/accordion/vite.config.ts new file mode 100644 index 0000000..c7a4f78 --- /dev/null +++ b/accordion/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; + +export default defineConfig({ + plugins: [react()], +}); \ No newline at end of file diff --git a/accordion/webflow.json b/accordion/webflow.json new file mode 100644 index 0000000..65ee4c8 --- /dev/null +++ b/accordion/webflow.json @@ -0,0 +1,10 @@ +{ + "library": { + "name": "Accordion", + "components": [ + "./src/**/*.webflow.@(js|jsx|mjs|ts|tsx)" + ], + "description": "A fully accessible accordion component that displays collapsible content sections with smooth slide animations. Each section consists of a clickable header with a title and an animated chevron icon that rotates 180 degrees when expanded. Supports two expansion modes: 'single' mode where opening one section automatically closes others, and 'multiple' mode where any number of sections can be open simultaneously. Features two visual variants: a 'bordered' style with visible borders around each section, and a 'flush' minimal style with subtle dividers. The first item is expanded by default on initial render. Fully keyboard accessible with Enter and Space key support, and implements proper ARIA attributes for screen readers. Responsive design ensures readability on all screen sizes.", + "id": "accordion" + } +} \ No newline at end of file