Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions toggle-switch/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# ToggleSwitch
A switch/toggle component for on/off boolean states with smooth animations and accessibility features.

## 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 the toggle switch |
| Size | Variant | medium | Visual size of the switch and text (small, medium, large) |
| Label Position | Variant | left | Position of the label relative to the switch (left, right) |
| Label | TextNode | Enable notifications | Main label text for the switch |
| Description | Text | Receive email updates about your account | Optional description text below the label |
| Default Checked | Boolean | false | Initial checked state of the switch |
| Is Disabled | Boolean | false | Disable the switch and prevent interaction |
| Show Icons | Boolean | false | Display on/off icons inside the thumb |
| Show Description | Visibility | — | Show or hide the description text |

## 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 | Track background when unchecked | #ffffff |
| --background-secondary | Track background on hover | #f5f5f5 |
| --text-primary | Label text color and unchecked thumb color | #1a1a1a |
| --text-secondary | Description text color | #737373 |
| --border-color | Track border color | #e5e5e5 |
| --accent-color | Track background when checked and focus ring color | #1a1a1a |
| --accent-text-color | Thumb color and icon color when checked | #ffffff |
| --border-radius | Track and thumb border radius | 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 Toggle Handler

Add custom logic when the toggle state changes:

```javascript
const toggle = document.querySelector('#my-toggle');
toggle.addEventListener('change', (e) => {
if (e.target.checked) {
// Enable feature
console.log('Feature enabled');
} else {
// Disable feature
console.log('Feature disabled');
}
});
```

### Programmatic State Control

Control the toggle state from your code:

```javascript
const toggle = document.querySelector('#my-toggle');
// Set checked state
toggle.checked = true;
// Trigger change event
toggle.dispatchEvent(new Event('change'));
```

## Dependencies

No external dependencies.
17 changes: 17 additions & 0 deletions toggle-switch/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ToggleSwitch</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; }
body { color: inherit; }
h1, h2, h3, h4, h5, h6 { color: inherit; }
</style>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
5 changes: 5 additions & 0 deletions toggle-switch/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Toggle Switch",
"description": "Accessible on/off toggle with label and disabled state",
"category": "Forms & Input"
}
25 changes: 25 additions & 0 deletions toggle-switch/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "toggle-switch",
"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"
}
}
1 change: 1 addition & 0 deletions toggle-switch/screenshot-brand.b64

Large diffs are not rendered by default.

Binary file added toggle-switch/screenshot-brand.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions toggle-switch/screenshot-dark.b64

Large diffs are not rendered by default.

Binary file added toggle-switch/screenshot-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions toggle-switch/screenshot-light.b64

Large diffs are not rendered by default.

Binary file added toggle-switch/screenshot-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
183 changes: 183 additions & 0 deletions toggle-switch/src/components/ToggleSwitch/ToggleSwitch.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/*
* Webflow Site Variables Used:
* - --background-primary: Track background when unchecked
* - --background-secondary: Hover states
* - --text-primary: Label text color
* - --text-secondary: Description text color
* - --border-color: Track border
* - --accent-color: Track background when checked
* - --accent-text-color: Thumb color and icon color when checked
* - --border-radius: Track and thumb rounding
*/

/* Box sizing reset */
.wf-toggleswitch *,
.wf-toggleswitch *::before,
.wf-toggleswitch *::after {
box-sizing: border-box;
}

/* Root element - inherit Webflow typography + default padding */
.wf-toggleswitch {
font-family: inherit;
color: inherit;
line-height: inherit;
padding: 24px;
display: flex;
align-items: flex-start;
gap: 12px;
width: fit-content;
}

/* Size variants - custom properties */
.wf-toggleswitch--size-small {
--wf-toggleswitch-track-width: 36px;
--wf-toggleswitch-track-height: 20px;
--wf-toggleswitch-thumb-size: 16px;
--wf-toggleswitch-thumb-offset: 2px;
--wf-toggleswitch-label-size: 14px;
--wf-toggleswitch-description-size: 12px;
}

.wf-toggleswitch--size-medium {
--wf-toggleswitch-track-width: 44px;
--wf-toggleswitch-track-height: 24px;
--wf-toggleswitch-thumb-size: 20px;
--wf-toggleswitch-thumb-offset: 2px;
--wf-toggleswitch-label-size: 16px;
--wf-toggleswitch-description-size: 14px;
}

.wf-toggleswitch--size-large {
--wf-toggleswitch-track-width: 52px;
--wf-toggleswitch-track-height: 28px;
--wf-toggleswitch-thumb-size: 24px;
--wf-toggleswitch-thumb-offset: 2px;
--wf-toggleswitch-label-size: 18px;
--wf-toggleswitch-description-size: 16px;
}

/* Label position variants */
.wf-toggleswitch--label-left {
flex-direction: row;
}

.wf-toggleswitch--label-right {
flex-direction: row-reverse;
}

/* Label wrapper */
.wf-toggleswitch-label-wrapper {
display: flex;
flex-direction: column;
gap: 4px;
}

/* Label */
.wf-toggleswitch-label {
font-size: var(--wf-toggleswitch-label-size);
font-weight: 500;
color: var(--text-primary, #1a1a1a);
line-height: 1.4;
}

/* Description */
.wf-toggleswitch-description {
font-size: var(--wf-toggleswitch-description-size);
color: var(--text-secondary, #737373);
line-height: 1.4;
}

/* Button */
.wf-toggleswitch-button {
background: transparent;
border: none;
padding: 0;
cursor: pointer;
flex-shrink: 0;
position: relative;
outline: none;
}

.wf-toggleswitch-button:focus-visible {
outline: 2px solid var(--accent-color, #1a1a1a);
outline-offset: 2px;
border-radius: calc(var(--border-radius, 8px) * 2);
}

/* Track */
.wf-toggleswitch-track {
display: block;
width: var(--wf-toggleswitch-track-width);
height: var(--wf-toggleswitch-track-height);
background: var(--background-primary, #ffffff);
border: 1px solid var(--border-color, #e5e5e5);
border-radius: calc(var(--wf-toggleswitch-track-height) / 2);
position: relative;
transition: background-color 0.2s, border-color 0.2s;
}

.wf-toggleswitch-button:hover .wf-toggleswitch-track {
background: var(--background-secondary, #f5f5f5);
}

.wf-toggleswitch--checked .wf-toggleswitch-track {
background: var(--accent-color, #1a1a1a);
border-color: var(--accent-color, #1a1a1a);
}

.wf-toggleswitch--checked .wf-toggleswitch-button:hover .wf-toggleswitch-track {
background: var(--accent-color, #1a1a1a);
opacity: 0.9;
}

/* Thumb */
.wf-toggleswitch-thumb {
display: flex;
align-items: center;
justify-content: center;
width: var(--wf-toggleswitch-thumb-size);
height: var(--wf-toggleswitch-thumb-size);
background: var(--text-primary, #1a1a1a);
border-radius: 50%;
position: absolute;
top: var(--wf-toggleswitch-thumb-offset);
left: var(--wf-toggleswitch-thumb-offset);
transition: transform 0.2s, background-color 0.2s;
}

.wf-toggleswitch--checked .wf-toggleswitch-thumb {
transform: translateX(calc(var(--wf-toggleswitch-track-width) - var(--wf-toggleswitch-thumb-size) - (var(--wf-toggleswitch-thumb-offset) * 2)));
background: var(--accent-text-color, #ffffff);
}

/* Icon */
.wf-toggleswitch-icon {
display: flex;
align-items: center;
justify-content: center;
color: var(--accent-text-color, #ffffff);
}

.wf-toggleswitch--checked .wf-toggleswitch-icon {
color: var(--accent-color, #1a1a1a);
}

.wf-toggleswitch-icon svg {
width: 12px;
height: 12px;
}

/* Disabled state */
.wf-toggleswitch--disabled {
opacity: 0.5;
pointer-events: none;
}

.wf-toggleswitch--disabled .wf-toggleswitch-button {
cursor: not-allowed;
}

.wf-toggleswitch--disabled .wf-toggleswitch-label {
color: var(--text-secondary, #737373);
}
Loading