Try Live
Add Docs
Rankings
Pricing
Enterprise
Docs
Install
Theme
Install
Docs
Pricing
Enterprise
More...
More...
Try Live
Rankings
Create API Key
Add Docs
BEEQ Design System
https://github.com/endava/beeq
Admin
BEEQ is a web component library and design system that provides reusable UI components with
...
Tokens:
57,088
Snippets:
601
Trust Score:
8.7
Update:
3 weeks ago
Context
Skills
Chat
Benchmark
84.4
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# BEEQ Design System BEEQ is a comprehensive web component library built with Stencil.js that provides a complete set of accessible, customizable UI components. The library delivers framework-agnostic custom elements that work seamlessly with vanilla JavaScript, Angular, React, and Vue applications. It includes over 40 production-ready components ranging from basic elements like buttons and inputs to complex components like dialogs, selects, and data tables. The design system follows a monorepo architecture using NX, with the core package (`@beeq/core`) containing all web components, and framework-specific wrappers (`@beeq/angular`, `@beeq/react`, `@beeq/vue`) providing enhanced integration with popular frameworks. Additionally, `@beeq/tailwindcss` offers an opinionated TailwindCSS preset for consistent styling. All components emit custom events prefixed with `bq` to prevent collisions with standard DOM events and support CSS custom properties for theming. ## Installation Install the core package for vanilla JavaScript or HTML usage. ```bash npm install @beeq/core ``` ```html <html> <head> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@beeq/core/dist/beeq/beeq.css" /> <script type="module" src="https://cdn.jsdelivr.net/npm/@beeq/core/dist/beeq/beeq.esm.js"></script> </head> <body> <bq-button appearance="primary">Click me!</bq-button> </body> </html> ``` ## Button Component The Button component is a fundamental UI element for user interactions, supporting multiple appearances (primary, secondary, link, text), sizes, and variants including loading and icon-only states. ```html <!-- Primary button with icon prefix --> <bq-button appearance="primary" size="medium"> <bq-icon name="arrow-circle-left" slot="prefix"></bq-icon> Go back </bq-button> <!-- Secondary button --> <bq-button appearance="secondary" size="large"> Cancel </bq-button> <!-- Link-style button --> <bq-button appearance="link" href="https://example.com" target="_blank"> Learn more </bq-button> <!-- Loading state button --> <bq-button appearance="primary" loading> Processing... </bq-button> <!-- Icon-only button --> <bq-button appearance="primary" only-icon label="Close menu"> <bq-icon name="x" slot="prefix"></bq-icon> </bq-button> <!-- Danger variant --> <bq-button appearance="primary" variant="danger"> Delete Account </bq-button> <script> const button = document.querySelector('bq-button'); button.addEventListener('bqClick', (event) => { console.log('Button clicked:', event.detail); }); button.addEventListener('bqFocus', () => console.log('Button focused')); button.addEventListener('bqBlur', () => console.log('Button blurred')); </script> ``` ## Input Component The Input component provides a flexible text input field with support for various types (text, password, email, number, tel, search, url), validation states, debounced input events, and helper text. ```html <!-- Basic input with label --> <bq-input name="username" placeholder="Enter your username"> <label slot="label">Username</label> </bq-input> <!-- Email input with validation status --> <bq-input name="email" type="email" placeholder="you@example.com" validation-status="error" required > <label slot="label">Email Address</label> <bq-icon name="envelope" slot="prefix"></bq-icon> <span slot="helper-text">Please enter a valid email address</span> </bq-input> <!-- Password input with custom clear button --> <bq-input name="password" type="password" minlength="8"> <label slot="label">Password</label> <bq-icon name="lock" slot="prefix"></bq-icon> <bq-icon name="eye" slot="suffix"></bq-icon> </bq-input> <!-- Number input with min/max constraints --> <bq-input name="quantity" type="number" min="1" max="100" value="10" debounce-time="300" > <label slot="label">Quantity</label> </bq-input> <!-- Search input with debounce --> <bq-input name="search" type="search" placeholder="Search..." debounce-time="500" > <bq-icon name="magnifying-glass" slot="prefix"></bq-icon> </bq-input> <script> const input = document.querySelector('bq-input[name="email"]'); input.addEventListener('bqInput', (event) => { console.log('Input value changed:', event.detail.value); }); input.addEventListener('bqChange', (event) => { console.log('Input committed:', event.detail.value); // Validate email const isValid = event.detail.value.includes('@'); input.validationStatus = isValid ? 'success' : 'error'; }); input.addEventListener('bqClear', () => { console.log('Input cleared'); }); </script> ``` ## Select Component The Select component provides a dropdown selection interface with support for single and multiple selection, search filtering, and customizable options with tags display for multi-select. ```html <!-- Basic single select --> <bq-select name="country" placeholder="Select a country"> <label slot="label">Country</label> <bq-option value="us">United States</bq-option> <bq-option value="uk">United Kingdom</bq-option> <bq-option value="ca">Canada</bq-option> <bq-option value="au">Australia</bq-option> </bq-select> <!-- Multiple selection with tags --> <bq-select name="skills" placeholder="Select skills" multiple max-tags-visible="3" > <label slot="label">Technical Skills</label> <span slot="helper-text"> <bq-icon name="info"></bq-icon> Select all that apply </span> <bq-option value="js">JavaScript</bq-option> <bq-option value="ts">TypeScript</bq-option> <bq-option value="react">React</bq-option> <bq-option value="vue">Vue.js</bq-option> <bq-option value="angular">Angular</bq-option> <bq-option value="node">Node.js</bq-option> </bq-select> <!-- Select with validation and custom panel height --> <bq-select name="priority" validation-status="warning" panel-height="200px" required > <label slot="label">Priority Level</label> <bq-icon name="flag" slot="prefix"></bq-icon> <bq-option value="low">Low Priority</bq-option> <bq-option value="medium">Medium Priority</bq-option> <bq-option value="high">High Priority</bq-option> <bq-option value="critical">Critical</bq-option> </bq-select> <script> const select = document.querySelector('bq-select[name="skills"]'); select.addEventListener('bqSelect', (event) => { console.log('Selected value:', event.detail.value); console.log('Selected item:', event.detail.item); }); select.addEventListener('bqClear', () => { console.log('Selection cleared'); }); // Programmatically clear selection select.clear(); // Reset to a specific value select.reset(['js', 'ts']); </script> ``` ## Checkbox Component The Checkbox component allows users to select one or more options, supporting checked, unchecked, and indeterminate states with full form integration. ```html <!-- Basic checkbox --> <bq-checkbox name="terms" value="accepted"> I agree to the Terms and Conditions </bq-checkbox> <!-- Checkbox with background on hover --> <bq-checkbox name="newsletter" value="subscribe" background-on-hover> Subscribe to newsletter </bq-checkbox> <!-- Indeterminate state (for "select all" scenarios) --> <bq-checkbox name="selectAll" value="all" indeterminate> Select All Items </bq-checkbox> <!-- Required checkbox in a form --> <form id="signup-form"> <bq-checkbox name="privacy" value="accepted" required form-validation-message="You must accept the privacy policy" > I have read and accept the Privacy Policy </bq-checkbox> </form> <!-- Disabled checkbox --> <bq-checkbox name="premium" value="enabled" disabled checked> Premium features (coming soon) </bq-checkbox> <script> const checkbox = document.querySelector('bq-checkbox[name="terms"]'); checkbox.addEventListener('bqChange', (event) => { console.log('Checkbox checked:', event.detail.checked); }); checkbox.addEventListener('bqFocus', () => { console.log('Checkbox focused'); }); // Programmatic control await checkbox.vClick(); // Simulate click await checkbox.vFocus(); // Set focus await checkbox.vBlur(); // Remove focus </script> ``` ## Dialog Component The Dialog component displays modal content with customizable headers, footers, and backdrop behavior, supporting programmatic show/hide methods and various events. ```html <!-- Basic dialog with header and footer --> <bq-dialog id="confirm-dialog" size="medium" border="m"> <h5 slot="title"> <bq-icon name="info" size="24"></bq-icon> Confirm Action </h5> <p> Are you sure you want to proceed with this action? This cannot be undone. </p> <div slot="footer" class="flex gap-xs"> <bq-button appearance="secondary" id="cancel-btn">Cancel</bq-button> <bq-button appearance="primary" variant="danger" id="confirm-btn"> Delete </bq-button> </div> </bq-dialog> <!-- Dialog without backdrop --> <bq-dialog id="info-dialog" disable-backdrop size="small"> <h5 slot="title">Quick Info</h5> <p>This is a non-modal dialog.</p> </bq-dialog> <!-- Dialog that prevents closing on ESC or backdrop click --> <bq-dialog id="critical-dialog" disable-close-esc-keydown disable-close-click-outside hide-close-button footer-appearance="highlight" > <h5 slot="title">Session Expiring</h5> <p>Your session will expire in 5 minutes. Please save your work.</p> <div slot="footer"> <bq-button appearance="primary">Extend Session</bq-button> </div> </bq-dialog> <!-- Trigger button --> <bq-button id="open-dialog-btn">Open Dialog</bq-button> <script> const dialog = document.querySelector('#confirm-dialog'); const openBtn = document.querySelector('#open-dialog-btn'); const cancelBtn = document.querySelector('#cancel-btn'); const confirmBtn = document.querySelector('#confirm-btn'); openBtn.addEventListener('bqClick', async () => { await dialog.show(); }); cancelBtn.addEventListener('bqClick', async () => { await dialog.cancel(); // Emits bqCancel event }); confirmBtn.addEventListener('bqClick', async () => { // Perform action await dialog.hide(); // Emits bqClose event }); // Dialog events dialog.addEventListener('bqOpen', () => console.log('Dialog opening')); dialog.addEventListener('bqAfterOpen', () => console.log('Dialog opened')); dialog.addEventListener('bqClose', () => console.log('Dialog closing')); dialog.addEventListener('bqAfterClose', () => console.log('Dialog closed')); dialog.addEventListener('bqCancel', () => console.log('Dialog cancelled')); </script> ``` ## Toast Component The Toast component displays brief, auto-dismissing messages for notifications, confirmations, and alerts with configurable duration and placement. ```html <!-- Basic toast types --> <bq-toast id="info-toast" type="info" time="5000"> Your changes have been saved. </bq-toast> <bq-toast id="success-toast" type="success" time="3000"> File uploaded successfully! </bq-toast> <bq-toast id="error-toast" type="error" time="6000"> Failed to connect to server. Please try again. </bq-toast> <bq-toast id="alert-toast" type="alert" time="4000"> Your session will expire in 5 minutes. </bq-toast> <bq-toast id="loading-toast" type="loading" time="0"> Processing your request... </bq-toast> <!-- Toast with custom placement --> <bq-toast id="positioned-toast" type="info" placement="top-right" border="m" > New message received </bq-toast> <!-- Toast without icon --> <bq-toast id="simple-toast" type="info" hide-icon> Simple notification </bq-toast> <script> // Show a single toast const infoToast = document.querySelector('#info-toast'); await infoToast.show(); // Use the toast portal for stacked notifications const successToast = document.querySelector('#success-toast'); await successToast.toast(); // Adds to portal and shows // Hide programmatically const loadingToast = document.querySelector('#loading-toast'); await loadingToast.show(); // Later, after operation completes await loadingToast.hide(); // Listen for visibility events infoToast.addEventListener('bqShow', () => console.log('Toast shown')); infoToast.addEventListener('bqHide', () => console.log('Toast hidden')); // Create and show toast dynamically function showNotification(message, type = 'info') { const toast = document.createElement('bq-toast'); toast.type = type; toast.innerHTML = message; document.body.appendChild(toast); toast.toast(); } showNotification('Welcome back!', 'success'); </script> ``` ## Tab Group Component The Tab Group component organizes content into tabbed sections with keyboard navigation support, orientation options, and customizable sizing. ```html <!-- Basic horizontal tabs --> <bq-tab-group value="tab-1"> <bq-tab tab-id="tab-1">Overview</bq-tab> <bq-tab tab-id="tab-2">Features</bq-tab> <bq-tab tab-id="tab-3">Pricing</bq-tab> <bq-tab tab-id="tab-4" disabled>Coming Soon</bq-tab> </bq-tab-group> <!-- Vertical tabs --> <bq-tab-group value="settings-1" orientation="vertical" size="small"> <bq-tab tab-id="settings-1">General</bq-tab> <bq-tab tab-id="settings-2">Security</bq-tab> <bq-tab tab-id="settings-3">Notifications</bq-tab> <bq-tab tab-id="settings-4">Integrations</bq-tab> </bq-tab-group> <!-- Tabs without divider --> <bq-tab-group value="nav-1" disable-divider placement="center" size="large"> <bq-tab tab-id="nav-1">Dashboard</bq-tab> <bq-tab tab-id="nav-2">Analytics</bq-tab> <bq-tab tab-id="nav-3">Reports</bq-tab> </bq-tab-group> <!-- Tab content panels --> <div id="tab-content"> <div id="panel-tab-1">Overview content here...</div> <div id="panel-tab-2" hidden>Features content here...</div> <div id="panel-tab-3" hidden>Pricing content here...</div> </div> <script> const tabGroup = document.querySelector('bq-tab-group'); const panels = document.querySelectorAll('#tab-content > div'); tabGroup.addEventListener('bqChange', (event) => { const { value, target } = event.detail; console.log('Tab changed to:', value); console.log('Tab element:', target); // Update visible panel panels.forEach(panel => { panel.hidden = panel.id !== `panel-${value}`; }); }); // With debounce for rapid navigation tabGroup.debounceTime = 100; </script> ``` ## Slider Component The Slider component provides a visual interface for selecting numeric values with support for single values and ranges, tooltips, and value indicators. ```html <!-- Basic single value slider --> <bq-slider name="volume" min="0" max="100" value="50"></bq-slider> <!-- Slider with value indicator --> <bq-slider name="brightness" min="0" max="100" value="75" enable-value-indicator ></bq-slider> <!-- Slider with tooltip --> <bq-slider name="opacity" min="0" max="1" step="0.1" value="0.5" enable-tooltip tooltip-always-visible ></bq-slider> <!-- Range slider for price filter --> <bq-slider name="price-range" type="range" min="0" max="1000" step="10" value="[100, 500]" gap="50" enable-value-indicator enable-tooltip ></bq-slider> <!-- Disabled slider --> <bq-slider name="locked" value="30" disabled ></bq-slider> <!-- Slider with debounced change events --> <bq-slider name="quality" min="0" max="100" value="80" debounce-time="300" ></bq-slider> <script> const slider = document.querySelector('bq-slider[name="price-range"]'); slider.addEventListener('bqChange', (event) => { const { value, el } = event.detail; console.log('Slider value:', value); // [100, 500] for range // Update price display document.querySelector('#min-price').textContent = `$${value[0]}`; document.querySelector('#max-price').textContent = `$${value[1]}`; }); slider.addEventListener('bqFocus', () => console.log('Slider focused')); slider.addEventListener('bqBlur', () => console.log('Slider blurred')); // Single value slider const volumeSlider = document.querySelector('bq-slider[name="volume"]'); volumeSlider.addEventListener('bqChange', (event) => { console.log('Volume:', event.detail.value); // Single number }); </script> ``` ## Angular Integration The Angular wrapper provides seamless integration with Angular's two-way data binding using `[(ngModel)]` and reactive forms support. ```typescript // app.module.ts import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { BrowserModule } from '@angular/platform-browser'; import { BeeQModule } from '@beeq/angular'; @NgModule({ declarations: [AppComponent], imports: [BeeQModule.forRoot(), BrowserModule, FormsModule], bootstrap: [AppComponent], }) export class AppModule {} ``` ```typescript // main.ts - Set icon path import { setBasePath } from '@beeq/core'; setBasePath('/assets/svg/'); ``` ```typescript // app.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: ` <bq-checkbox name="terms" [(ngModel)]="termsAccepted" (bqChange)="onTermsChange()" > Accept Terms & Conditions </bq-checkbox> <bq-slider min="0" max="100" type="range" debounce-time="250" [(ngModel)]="priceRange" (bqChange)="onPriceChange()" ></bq-slider> <bq-input name="email" type="email" [(ngModel)]="email" (bqInput)="onEmailInput($event)" > <label slot="label">Email</label> </bq-input> `, }) export class AppComponent { termsAccepted = false; priceRange = [20, 80]; email = ''; onTermsChange() { console.log('Terms accepted:', this.termsAccepted); } onPriceChange() { console.log('Price range:', this.priceRange); } onEmailInput(event: CustomEvent) { console.log('Email input:', event.detail.value); } } ``` ```typescript // Standalone component usage import { Component } from '@angular/core'; import { BqButton, BqCard, BqInput } from '@beeq/angular/standalone'; @Component({ selector: 'app-signup', standalone: true, imports: [BqButton, BqCard, BqInput], template: ` <bq-card> <bq-input name="email" [value]="email" (bqChange)="onEmailChange($event)"> <label slot="label">Your email</label> </bq-input> <bq-button (bqClick)="subscribe()">Subscribe</bq-button> </bq-card> `, }) export class SignupComponent { email = ''; onEmailChange(event: CustomEvent<{ value: string }>) { this.email = event.detail.value; } subscribe() { console.log('Subscribing:', this.email); } } ``` ## React Integration The React wrapper converts web components to native React components with proper event handling and type definitions. ```tsx // App.tsx import React, { useState } from 'react'; import { BqButton, BqInput, BqSelect, BqOption, BqCheckbox } from '@beeq/react'; function App() { const [email, setEmail] = useState(''); const [country, setCountry] = useState(''); const [newsletter, setNewsletter] = useState(false); const handleSubmit = () => { console.log({ email, country, newsletter }); }; return ( <form> <BqInput name="email" type="email" value={email} placeholder="Enter your email" onBqChange={(e) => setEmail(e.detail.value as string)} onBqClear={() => setEmail('')} > <label slot="label">Email Address</label> </BqInput> <BqSelect name="country" value={country} placeholder="Select your country" onBqSelect={(e) => setCountry(e.detail.value as string)} > <label slot="label">Country</label> <BqOption value="us">United States</BqOption> <BqOption value="uk">United Kingdom</BqOption> <BqOption value="ca">Canada</BqOption> </BqSelect> <BqCheckbox name="newsletter" checked={newsletter} onBqChange={(e) => setNewsletter(e.detail.checked)} > Subscribe to newsletter </BqCheckbox> <BqButton appearance="primary" onBqClick={handleSubmit} > Submit </BqButton> </form> ); } export default App; ``` ```typescript // main.tsx - Configure icons path import { setBasePath } from '@beeq/core/dist/components'; setBasePath('icons/svg'); ``` ## Vue Integration The Vue wrapper provides v-model support and seamless event handling for Vue 3 applications. ```vue <script setup lang="ts"> import { ref } from 'vue'; const name = ref(''); const selectedOption = ref(''); const agreedToTerms = ref(false); const sliderValue = ref(50); </script> <template> <form @submit.prevent="handleSubmit"> <bq-input name="name" placeholder="Enter your name" v-model="name" @bqClear="name = ''" > <label slot="label">Full Name</label> <bq-icon name="user" slot="prefix"></bq-icon> </bq-input> <bq-select name="category" placeholder="Select a category" v-model="selectedOption" > <label slot="label">Category</label> <bq-option value="tech">Technology</bq-option> <bq-option value="health">Health</bq-option> <bq-option value="finance">Finance</bq-option> </bq-select> <bq-checkbox name="terms" v-model="agreedToTerms" > I agree to the terms </bq-checkbox> <bq-slider name="rating" min="0" max="100" v-model="sliderValue" enable-value-indicator ></bq-slider> <bq-button appearance="primary" type="submit"> Submit </bq-button> </form> </template> ``` ```typescript // vite.config.ts import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; export default defineConfig({ plugins: [ vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.includes('bq-'), }, }, }), ], }); ``` ## TailwindCSS Preset The BEEQ TailwindCSS preset provides design tokens, typography defaults, and CSS reset for consistent styling. ```javascript // tailwind.config.js const beeqPreset = require('@beeq/tailwindcss'); module.exports = { content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], presets: [beeqPreset], }; ``` ```typescript // tailwind.config.ts with typography import plugin from 'tailwindcss/plugin'; import { default as beeqPreset, TYPOGRAPHY_DEFAULT } from '@beeq/tailwindcss'; import type { Config } from 'tailwindcss'; export default { content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], presets: [beeqPreset], plugins: [ plugin(function ({ addBase }) { addBase({ ...TYPOGRAPHY_DEFAULT }); }), ], } satisfies Config; ``` ```css /* main.css - Add fonts and Tailwind directives */ @import url('https://fonts.googleapis.com/css2?family=Outfit:wght@100;300;400;600;700&display=swap'); @tailwind base; @tailwind components; @tailwind utilities; ``` ## CSS Custom Properties Theming All components support extensive CSS custom property customization for theming and styling adjustments. ```css /* Custom theme overrides */ :root { /* Button customization */ --bq-button--border-radius: var(--bq-radius--m); --bq-button--medium-paddingX: 1.5rem; --bq-button--medium-paddingY: 0.75rem; /* Input customization */ --bq-input--background-color: #f8fafc; --bq-input--border-color: #e2e8f0; --bq-input--border-color-focus: #3b82f6; --bq-input--border-radius: var(--bq-radius--s); /* Dialog customization */ --bq-dialog--background: #ffffff; --bq-dialog--box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); --bq-dialog--border-radius: var(--bq-radius--l); /* Toast customization */ --bq-toast--background: #1e293b; --bq-toast--border-radius: var(--bq-radius--m); /* Slider customization */ --bq-slider--progress-color: #3b82f6; --bq-slider--trackarea-color: #e2e8f0; --bq-slider--thumb-size: 20px; } /* Component-specific overrides using ::part() */ bq-button::part(button) { font-weight: 600; text-transform: uppercase; } bq-input::part(input) { font-size: 1rem; } bq-dialog::part(panel) { max-width: 600px; } ``` ## Summary BEEQ Design System provides a production-ready component library that excels in building accessible, responsive web applications across any JavaScript framework. The core library delivers framework-agnostic web components with consistent APIs and comprehensive event systems, while framework-specific wrappers provide native integration patterns like Angular's `[(ngModel)]`, React's event handlers (`onBqClick`, `onBqChange`), and Vue's `v-model` directives. The design system is particularly well-suited for enterprise applications requiring consistent UI patterns, form-heavy interfaces with complex validation requirements, and projects that need to support multiple frontend frameworks from a single component library. Integration typically involves installing the core package plus framework wrapper, configuring icon paths, and importing components as needed. The extensive CSS custom property support enables deep theming customization without modifying component source code.