### How to Use
#FLOATING PORTAL
- Enable `Settings > General > Expose the option to add an ID attribute to HTML elements`.
- Create a normal Bubble Group with the elements you want inside the floating container.
- Set that Group's ID attribute, for example `my-floating-actions`.
- Add the UI Kit Pro Floating Portal element anywhere on the page.
- Set `target_element_id` to `my-floating-actions`.
#TOGGLE BUTTON
The element is resizable through the Bubble canvas. Its track fills the element box, and the thumb is recalculated with `ResizeObserver`.
# ICON TOOLTIP
Very easy and intuitive.
##Navigation Menu
**Using Option Sets (Recommended):**
Create an Option Set called "Menu" with attributes:
- `id` (text) - Unique identifier
- `label` (text) - Display text
- `icon` (text) - Lucide icon name
- `parent_id` (text) - Parent item ID for submenus
In the `menu_items` property, use "Format as text":
**1. Add Lucide Icons to your app:**
Go to Settings → SEO/metatags → Script/meta tags in the header and add:
```html
<script src="
https://unpkg.com/lucide@latest"></script>
```
```
See how:
🔗
https://66d6f9b26f6e89e4f2c2e39dddbbb195.cdn.bubble.io/f1770299703745x319373495180062660/format%20content%20as%20a%20text.pngJSON Exemple:
[
{
"id": "dashboard",
"label": "Dashboard",
"icon": "layout-dashboard",
"badge": "New",
"badge_color": "#22c55e"
},
{
"id": "analytics",
"label": "Analytics",
"icon": "bar-chart-3",
"children": [
{
"id": "analytics-overview",
"label": "Overview",
"icon": "eye"
},
{
"id": "analytics-reports",
"label": "Reports",
"icon": "file-text"
},
{
"id": "analytics-realtime",
"label": "Real-time",
"icon": "activity",
"badge": "Live",
"badge_color": "#ef4444"
}
]
},
{
"id": "users",
"label": "Users",
"icon": "users",
"badge": "128",
"badge_color": "#6366f1",
"children": [
{
"id": "users-list",
"label": "All Users",
"icon": "list"
},
{
"id": "users-add",
"label": "Add User",
"icon": "user-plus"
},
{
"id": "users-roles",
"label": "Roles & Permissions",
"icon": "shield"
}
]
},
{
"type": "divider"
},
{
"id": "products",
"label": "Products",
"icon": "shopping-bag",
"children": [
{
"id": "products-catalog",
"label": "Catalog",
"icon": "grid"
},
{
"id": "products-inventory",
"label": "Inventory",
"icon": "package"
},
{
"id": "products-categories",
"label": "Categories",
"icon": "folder"
}
]
},
{
"id": "orders",
"label": "Orders",
"icon": "shopping-cart",
"badge": "5",
"badge_color": "#f59e0b"
},
{
"id": "messages",
"label": "Messages",
"icon": "mail",
"badge": "12",
"badge_color": "#3b82f6"
},
{
"type": "divider"
},
{
"id": "settings",
"label": "Settings",
"icon": "settings",
"children": [
{
"id": "settings-general",
"label": "General",
"icon": "sliders"
},
{
"id": "settings-security",
"label": "Security",
"icon": "lock"
},
{
"id": "settings-notifications",
"label": "Notifications",
"icon": "bell"
},
{
"id": "settings-integrations",
"label": "Integrations",
"icon": "plug"
}
]
},
{
"id": "help",
"label": "Help & Support",
"icon": "help-circle"
}
]
---
**3. Action Button Setup:**
Simply drag the Action Button element and configure:
- Choose `button_type` preset or use `custom`
- Set variant, size, and colors
- Enable `show_confirmation` for destructive actions
- Use `btn_clicked` event for your workflow
# Skeleton Loader
- Place the Skeleton element where the real content will appear, with the same approximate size.
- In the real content (Group, Repeating Group, Text, Image), use a conditional `When (your data source) is empty` → element is visible = no.
- In the Skeleton element, use the opposite condition: `When (your data source) is empty` → visible = yes. As soon as data arrives, the skeleton hides and the real content fades in.
Variants at a glance:
- **text** — N stacked lines; last line shorter (60% by default) for a natural look. Use for paragraphs and lists.
- **rect** — single solid block. Use for images, charts, and arbitrary blocks.
- **circle** — single round block. Use for avatars and icons.
- **row** — N horizontal cells aligned. Use as a Repeating Group row placeholder while data is fetching.
- **avatar_text** — circle + two short lines beside it. Use for profile/contact list items.
- **card** — cover block on top + title + 3 description lines. Use for product/article cards.
Notes:
- The element exposes `role="status"` and `aria-live="polite"` so screen readers announce that content is loading.
- Animation is automatically disabled when the user has `prefers-reduced-motion: reduce` enabled in their OS.
- Shimmer is the default because it scans better against busy backgrounds; pulse is lighter visually for compact lists.
- All warnings are prefixed `[UIKitPro/Skeleton]` in the browser console.
# Toast Container
1. Drop the Toast Container anywhere on the page (it is invisible — renders into `<body>` directly, fixed-positioned).
2. From any workflow (button click, API response, custom event), call `Toast Container's Show toast` with the message and type.
3. For an error pattern: set `type = error`, `duration_ms = 0` (sticky), and let the user dismiss.
4. For a success pattern after a Xano save: `type = success`, `duration_ms = 3000`, no title.
You can place multiple Toast Containers on the same page (e.g. one `top-right` for successes and another `bottom-center` for errors). Each container has its own actions and configuration.
## Notes
- Container renders into `<body>` with `position: fixed`. It is not affected by Bubble's responsive layout or by being inside a Group.
- `role` and `aria-live` adapt automatically: `error`/`warning` use `assertive`, `success`/`info` use `polite`. Screen readers announce errors immediately.
- Animation is automatically disabled when `prefers-reduced-motion: reduce` is set in the OS.
- All logs are prefixed `[UIKitPro/Toast]`.
- The text content is set via `textContent`, so user-supplied messages cannot inject HTML.
# Progress
- Indeterminate mode ignores `value` and just animates a sliding bar (linear) or a spinning arc (circular). Use it for "loading…" states with unknown duration.
- `label_position = inside` uses `mix-blend-mode: difference` so the text stays readable against both the track and the fill. Works in all modern browsers.
- `gradient = yes` + a vibrant `gradient_color` is the most "premium" look for hero progress bars (onboarding, file upload).
- ARIA: determinate sets `aria-valuenow / aria-valuemin / aria-valuemax`; indeterminate sets `aria-busy="true"`.
- Animations respect `prefers-reduced-motion: reduce`.
- All warnings prefixed `[UIKitPro/Progress]`.
# Tabs
For each tab, create a Group on the page that holds that tab's content. On each Group add a conditional:
- `When (Tabs)'s active_tab_id is "profile"` → element is visible = yes
- Otherwise: element is visible = no
Repeat for each tab id. The Tabs element itself only renders the strip — the panels are normal Bubble Groups.
## Notes
- The underline variant uses an absolutely-positioned `__indicator` that slides via `translate3d` for smooth animation. It is re-measured on resize via `ResizeObserver` and on every tab change.
- Horizontal mode auto-scrolls active tab into view and shows fade gradients on the edges when content overflows.
- Keyboard nav follows the [WAI-ARIA Authoring Practices for tabs](
https://www.w3.org/WAI/ARIA/apg/patterns/tabs/): Arrow keys move focus, Home/End jump to ends, Enter is implicit on `<button>`.
- `font-variant-numeric: tabular-nums` is applied to badges so digit widths don't jiggle.
- All logs are prefixed `[UIKitPro/Tabs]`.
- For Lucide icons, add `<script src="
https://unpkg.com/lucide@latest"></script>` to the page HTML Header (or App Header). Falls back to a simple square placeholder if Lucide isn't loaded.
json
[
{ "id": "profile", "label": "Perfil", "icon": "user" },
{ "id": "settings", "label": "Configurações", "icon": "settings" },
{ "id": "alerts", "label": "Alertas", "icon": "bell", "badge": "3" },
{ "id": "billing", "label": "Faturamento", "icon": "credit-card", "disabled": true }
]
#Dialog Box
Workflow on a "Delete account" button:
1. **(Dialog Box)'s Show dialog**:
- `title` = `Excluir conta?`
- `message` = `Esta ação é permanente e não pode ser desfeita.`
- `icon` = `error`
- `buttons` = `[{"id":"delete","label":"Excluir","variant":"danger"},{"id":"export","label":"Exportar dados antes","variant":"secondary"},{"id":"cancel","label":"Cancelar","variant":"ghost","align":"left"}]`
2. Add workflow events:
- **(Dialog)'s button_clicked** with condition `last_clicked_button_id is "delete"` → run the delete logic.
- Another **button_clicked** with condition `last_clicked_button_id is "export"` → run the export.
- The `cancel` button just closes; no condition needed unless you want to track.
## Notes
- All text content is rendered via `textContent`, so user input cannot inject HTML.
- The first focusable button gets focus on open; ESC and click-outside are configurable.
- Tab and Shift+Tab cycle through the buttons (focus trap).
- The previously focused element is restored on close.
- Animation is automatically disabled when `prefers-reduced-motion: reduce` is set in the OS.
- All logs prefixed `[UIKitPro/Dialog]`.
Json Schema
[
{ "id": "save", "label": "Salvar", "variant": "primary" },
{ "id": "discard", "label": "Descartar", "variant": "danger" },
{ "id": "cancel", "label": "Cancelar", "variant": "ghost", "align": "left" }
]
# Stepper
For each step, create a Group on the page with its content. Add a conditional:
- `When (Stepper)'s current_step_id is "account"` → element is visible = yes
- Otherwise: visible = no
Inside each panel, add the navigation buttons:
- **Voltar** → workflow → `(Stepper)'s Previous`
- **Avançar** → validate (if needed) → workflow → `(Stepper)'s Next`
- **Concluir** (only on the last step) → save data → `(Stepper)'s Next` (fires `completed`)
## Notes
- `dots` variant hides the number/icon and shows just a small filled circle — minimalist for compact pagination/onboarding.
- `iconed` shows the icon when pending and the check mark when completed; the icon "transforms" into a check on advance. Looks great with Lucide loaded.
- Vertical orientation uses a left-side rail with the marker on top of each panel — perfect for long forms with descriptions.
- Click-to-navigate is OFF by default to avoid skipping validation. Turn it on when steps are independent (e.g., user profile sections).
- `mark_completed_on_advance = no` lets you control completion entirely from workflows — useful when a step is only "done" after a Xano call returns successfully.
- All warnings prefixed `[UIKitPro/Stepper]`.
Json Schema
{ "id": "account", "label": "Conta", "description": "Dados de acesso", "icon": "user" },
{ "id": "profile", "label": "Perfil", "description": "Informações pessoais", "icon": "id-card" },
{ "id": "billing", "label": "Pagamento", "description": "Cartão e endereço", "icon": "credit-card" },
{ "id": "review", "label": "Revisão", "description": "Confirme tudo", "icon": "check" }
]
# ComboBox
- `aria-autocomplete="list"`, `role="combobox"`, `aria-activedescendant` follow WAI-ARIA combobox pattern.
- Dropdown auto-flips above when there isn't space below the field.
- Single-select restores the chosen label in the field (and clears the input text) when the dropdown closes — so the user always sees what is selected.
- Multi-select keeps the dropdown open after picking, so the user can pick several in a row.
- For very large lists, prefer `min_search_length = 2` so the list only renders after filtering.
- All warnings prefixed `[UIKitPro/Combobox]`.
## Future enhancements (V2)
- Async loading (debounced search → workflow → updates options dynamically)
- Option groups with headers
- "Create new option" when no match
- Virtual scrolling for 10k+ items
Json Schema
[
{ "value": "BR-AC", "label": "Acre", "description": "Norte" },
{ "value": "BR-AL", "label": "Alagoas", "description": "Nordeste" },
{ "value": "BR-AM", "label": "Amazonas", "description": "Norte" },
{ "value": "BR-DF", "label": "Distrito Federal", "description": "Centro-Oeste" },
{ "value": "BR-SP", "label": "São Paulo", "description": "Sudeste", "icon_url": "
https://flagcdn.com/w20/br.png" }
]
# Period Picker
1. Drop the Period Picker on the top of the dashboard. Set `period_type = month` (or whatever default).
2. In your data sources (Repeating Group, charts, etc.), filter by:
- `created_at ≥ (Period Picker)'s start_date`
- `created_at ≤ (Period Picker)'s end_date`
3. (Optional) Add Tabs above to let the user switch period type (`Mês` / `Bim` / `Tri` / `Sem` / `Ano`). On `tab_changed`, call `(Period Picker)'s Set period type` with the corresponding value.
4. (Optional) Add buttons elsewhere (e.g. "Mês anterior", "Próximo mês") that call `Previous period` / `Next period`.
## Notes
- All dates are produced as native JS `Date` objects, which Bubble accepts directly into date-typed fields and states.
- `start_date` is 00:00:00 local time; `end_date` is 23:59:59.999 local time — designed so a `≤ end_date` filter is inclusive of the last day.
- `min_date` / `max_date` constrain both arrow navigation and the popover grid (out-of-range cells are disabled).
- Dropdown popover auto-flips above when there isn't space below.
- For `year` type, the dropdown shows a 12-year grid centered on the current year; the year-nav arrows shift the whole grid by 12.
- Keyboard: focus the display (in dropdown mode) and press `Enter` or `Space` to toggle; `Escape` closes.
- All warnings prefixed `[UIKitPro/Period]`.
# Slider
## Tooltip format examples
| `tooltip_format` | If `value = 1500.5` | Result |
|---|---|---|
| `{value}` | | `1500.5` |
| `R$ {value}` | | `R$ 1500.5` |
| `{value}%` | | `1500.5%` |
| `{value} km/h` | | `1500.5 km/h` |
| `~{value} pts` | | `~1500.5 pts` |
## Notes
- Click on the track jumps the **nearest** handle there (range mode) or moves the single handle.
- Range mode prevents the two handles from crossing.
- Touch-friendly: uses Pointer Events so mobile drag works without special config.
- `value_committed` fires only on drag end / keyboard release — perfect for triggering API searches without flooding during drag.
- `prefers-reduced-motion` disables transitions so the handle moves instantly with the pointer.
- All warnings prefixed `[UIKitPro/Slider]`.