Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Team Roles Dialog Block for Svelte

A members page with its roles dialog shown open: seven members with a select-all that shows all, none or a dash, a role select with a line about each role, a Can and Cannot list that changes with it, and a guard that refuses to leave the workspace without an Owner.

Framework
UI library

Preview

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Overview

Let an admin change several roles at once and read what the new role allows before applying it, with this team roles dialog for Svelte with Ark UI. It opens from the Members page of Teamfern, a project workspace: the page's header holds an outline Change roles Button, and the members table, a row of initials, a name bar and the role as text for each of seven people, is drawn under a soft scrim inside a hairline frame with a plus mark on each corner. The Dialog has the seven members at the start, each with a Checkbox, initials, a name and the current role in a Badge, and a select-all that shows all, none or a dash. At the end a Native Select chooses the role, a line says what it is for, and a list reads Can or Cannot for inviting people, managing billing, deleting projects and editing content. A sentence says what will happen, and a guard refuses any change that would leave no Owner. Apply is the only Shine Button; it writes the new roles on the drawn table. For preview the dialog is drawn open in place when the page loads; the header button opens the real dialog, with the same ticks. For workspaces, admin consoles and any app that has roles.

FAQ

Why is the dialog already open, and what do I keep?

So you can see it without a click. On load the dialog is a copy drawn in place over the page: a native dialog element that isn't modal, so nothing is trapped, hidden or focused, and the page around the preview keeps working. The Change roles button opens the real Dialog, modal, with its focus trap, Escape and focus return, and the copy stays closed until a reload. Both show the same RolesFields component and share the block's state, so a member ticked in the copy is ticked in the real one. In your app keep the Dialog and drop the copy: the "in-place" state, the scrim and the dialog element inside the frame.

What does the last-owner guard do?

After every tick and every choice the dialog counts the Owners the workspace would have if you applied now: the ticked members get the new role, the others keep theirs. If that is zero, the select turns invalid, an alert says "A workspace needs at least one Owner", and Apply stays on the page with a quiet look. Pressing it names the problem and moves focus to the role select. Untick the Owner, or give the Owner role to someone else in the same change, and the guard lets go.

How does the select-all show three states?

It reads the number of ticked members: none is unchecked, all seven is checked, anything between is a dash, which assistive technology hears as mixed. Pressing it ticks everyone, or clears everyone when all are ticked. The rows show what would happen to each ticked member ("Becomes Editor") in place of their email, so you can check the selection without leaving the list, and a line counts "3 of 7 selected".

Is it accessible from the keyboard?

Yes. The real dialog is named by its title and described by its sentence, focus starts on the select-all box, and Escape, Cancel and the close button return focus to the Change roles button. Every box is named for its member, the role select is a native select, the permissions are a description list with the answer in words as well as an icon, and what changes is spoken once in a polite status line (inside the dialog while it is open). The trigger is the button wrapped in DialogTrigger with asChild, and the dialog is controlled with open and onOpenChange.

What stays local, and how do I wire it for real?

Everything. The seven members, the four roles and what each can do are typed in the file, and Apply only changes this block's state and the drawn table. In your app send the change to your API, enforce the last-owner rule on the server as well (the page's guard is a courtesy, not a lock), and show the permissions from the same table the server checks.

How does it look on a phone?

The copy shown on load docks to the foot of the page as a bottom sheet with its buttons pinned and the content scrolling above them: the members first, then the role, what it allows, the sentence and the guard in one column. The real dialog keeps the Dialog's own phone layout, a sheet stuck to the bottom of the screen.