shadcn.io is not affiliated with official shadcn/ui
Achievement Unlock Dialog Block for Vue
A chess trainer's profile with its achievement dialog shown open over a drawn player card: a medal that fills in, the tier in words and dots, XP toward level 8, two achievements close to unlocking and a Copy brag line button.
Installation
Overview
Celebrate a milestone without a confetti cannon, with this achievement unlock dialog for Vue with Ark UI. It opens from the profile page of Rookline, a chess trainer: the header holds an outline Button, and the player card it opens over is drawn under a soft scrim inside a hairline frame with a plus mark on each corner, with a rating, three badge slots and one marked New. The Dialog shows a medal drawn in SVG that fills from an outline when it opens, the achievement's name, the tier in words and in dots (Rare, tier 2 of 3), a Progress bar for XP (1,240 of 1,500, 260 to go) and a Next up list of two locked achievements, each with a bar of its own. A Copy Button copies a typed brag line, and Continue, the only Shine Button, fills the third badge slot on the profile behind it. For preview the dialog is drawn open in place when the page loads; the header button opens the real Dialog, with the same state. For games, learning apps, fitness trackers and any product that rewards progress.
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 profile: a native dialog element that isn't modal, so nothing is trapped, hidden or focused, and the page around the preview keeps working. The View new achievement button in the header 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 UnlockBody component and share the block's state, so what you set in the copy is there 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.
Where do the tier, the XP and the bars come from?
From typed constants at the top of the file: ACHIEVEMENT (name, note, tier, tier count, reward), XP (level, XP now, XP for the next level) and NEXT_UP (two goals with have and need). Everything shown is derived from them: the dots are tier steps filled out of three, 260 to go is the next level minus the current XP, and each bar is have over need. Replace the constants with the unlock payload your server sends, and compute the XP on the server so the client only draws it.
Does the medal animate, and what happens with reduced motion?
When the dialog opens, the medal's disc fills from an outline to solid foreground and the knight turns from foreground to background, a 700 ms CSS transition that starts from the starting: style. With reduced motion on, the transition is off and the medal simply appears filled. There is no confetti and nothing loops. The medal is decoration and hidden from assistive technology; the achievement's name, its note and its tier are all in text.
What does Copy brag line do?
It puts one typed sentence on the clipboard and nothing else: "Unlocked Knight fork (Rare) on Rookline: three wins with a knight fork. Level 8 is 260 XP away." Nothing is posted anywhere. The button says Copied to screen readers, or says it couldn't copy if the browser refuses, for example on a page that isn't served over HTTPS. To share for real, build the sentence from the unlock payload and hand it to your share sheet or your own posting API.
What stays local, and how do I wire it for real?
Everything. Continue marks the badge as claimed in local state, fills the third slot on the drawn profile and says so in a polite status message that adds nothing was posted. Escape and the close button leave the badge unclaimed. In your app send the claim to your server from claim, show the real badge from the response, and fire your own analytics event for the unlock.
Is it accessible from the keyboard?
Yes. The real dialog is named by its title and described by its sentence, focus is trapped while it is open, and Escape and the close button return focus to the header button. The trigger is the button wrapped in DialogTrigger with asChild, and the dialog is controlled with open and onOpenChange. The copy shown on load moves no focus; Escape inside it and its close button close it and put focus back on the header button. Continue and Copy brag line are the two buttons in the footer, in that reading order on a phone.
How does it look on a phone?
The copy shown on load docks to the foot of the frame as a bottom sheet with Continue pinned below the scrolling body, and the medal sits at the start of its text instead of above it so the first screen shows the name and the tier. The real dialog keeps the Dialog's own phone layout: a sheet stuck to the bottom of the screen.