@apex-stack/core 0.2.2 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template x-data="{ isExpanded: false }">
3
+ <div class="w-full overflow-hidden rounded-radius border border-outline bg-surface-alt/40 text-on-surface dark:border-outline-dark dark:bg-surface-dark-alt/50 dark:text-on-surface-dark">
4
+ <button id="accordionControl" type="button"
5
+ class="flex w-full items-center justify-between gap-4 bg-surface-alt p-4 text-left underline-offset-2 hover:bg-surface-alt/75 focus-visible:bg-surface-alt/75 focus-visible:underline focus-visible:outline-hidden dark:bg-surface-dark-alt dark:hover:bg-surface-dark-alt/75 dark:focus-visible:bg-surface-dark-alt/75"
6
+ aria-controls="accordionPanel" x-on:click="isExpanded = ! isExpanded"
7
+ x-bind:class="isExpanded ? 'text-on-surface-strong dark:text-on-surface-dark-strong font-bold' : 'text-on-surface dark:text-on-surface-dark font-medium'"
8
+ x-bind:aria-expanded="isExpanded ? 'true' : 'false'">
9
+ <slot name="title">Accordion title</slot>
10
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke-width="2"
11
+ stroke="currentColor" class="size-5 shrink-0 transition" aria-hidden="true"
12
+ x-bind:class="isExpanded ? 'rotate-180' : ''">
13
+ <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
14
+ </svg>
15
+ </button>
16
+ <div x-cloak x-show="isExpanded" id="accordionPanel" role="region" aria-labelledby="accordionControl" x-collapse>
17
+ <div class="p-4 text-sm sm:text-base text-pretty">
18
+ <slot>Accordion panel content</slot>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ </template>
@@ -0,0 +1,27 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template x-data="{ alertIsVisible: true }">
3
+ <div x-show="alertIsVisible"
4
+ class="relative w-full overflow-hidden rounded-radius border border-info bg-surface text-on-surface dark:bg-surface-dark dark:text-on-surface-dark"
5
+ role="alert" x-transition:leave="transition ease-in duration-300" x-transition:leave-start="opacity-100 scale-100"
6
+ x-transition:leave-end="opacity-0 scale-90">
7
+ <div class="flex w-full items-center gap-2 bg-info/10 p-4">
8
+ <div class="bg-info/15 text-info rounded-full p-1" aria-hidden="true">
9
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-6"
10
+ aria-hidden="true">
11
+ <path fill-rule="evenodd"
12
+ d="M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0Zm-7-4a1 1 0 1 1-2 0 1 1 0 0 1 2 0ZM9 9a.75.75 0 0 0 0 1.5h.253a.25.25 0 0 1 .244.304l-.459 2.066A1.75 1.75 0 0 0 10.747 15H11a.75.75 0 0 0 0-1.5h-.253a.25.25 0 0 1-.244-.304l.459-2.066A1.75 1.75 0 0 0 9.253 9H9Z"
13
+ clip-rule="evenodd" />
14
+ </svg>
15
+ </div>
16
+ <div class="ml-2 text-xs font-medium sm:text-sm">
17
+ <slot></slot>
18
+ </div>
19
+ <button type="button" @click="alertIsVisible = false" class="ml-auto" aria-label="dismiss alert">
20
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" stroke="currentColor"
21
+ fill="none" stroke-width="2.5" class="w-4 h-4 shrink-0">
22
+ <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
23
+ </svg>
24
+ </button>
25
+ </div>
26
+ </div>
27
+ </template>
@@ -0,0 +1,4 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template>
3
+ <span class="flex size-14 items-center justify-center overflow-hidden rounded-full border border-outline bg-surface-alt text-2xl font-bold tracking-wider text-on-surface/80 dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark/80"><slot>JS</slot></span>
4
+ </template>
@@ -0,0 +1,6 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template x-data>
3
+ <span class="inline-flex items-center gap-1 rounded-radius border border-outline bg-surface-alt px-2 py-0.5 text-xs font-medium text-on-surface dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark">
4
+ <slot>Badge</slot>
5
+ </span>
6
+ </template>
@@ -0,0 +1,11 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template x-data="{ bannerIsVisible: true }">
3
+ <div x-show="bannerIsVisible" class="relative flex border-outline bg-surface-alt p-4 text-on-surface dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark border-b">
4
+ <p class="px-6 text-xs sm:text-sm text-pretty mx-auto"><slot></slot></p>
5
+ <button type="button" @click="bannerIsVisible = false" class="absolute top-1/2 -translate-y-1/2 right-4" aria-label="dismiss banner">
6
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" stroke="currentColor" fill="none" stroke-width="2.5" class="size-4 shrink-0">
7
+ <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
8
+ </svg>
9
+ </button>
10
+ </div>
11
+ </template>
@@ -0,0 +1,20 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template>
3
+ <nav class="text-sm font-medium text-on-surface dark:text-on-surface-dark" aria-label="breadcrumb">
4
+ <ol class="flex flex-wrap items-center gap-1">
5
+ <li class="flex items-center gap-1">
6
+ <a href="#" class="hover:text-on-surface-strong dark:hover:text-on-surface-dark-strong">Home</a>
7
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" aria-hidden="true" stroke-width="2" stroke="currentColor" class="size-4">
8
+ <path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
9
+ </svg>
10
+ </li>
11
+ <li class="flex items-center gap-1">
12
+ <a href="#" class="hover:text-on-surface-strong dark:hover:text-on-surface-dark-strong">Components</a>
13
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" aria-hidden="true" stroke-width="2" stroke="currentColor" class="size-4">
14
+ <path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
15
+ </svg>
16
+ </li>
17
+ <li class="flex items-center text-on-surface-strong gap-1 font-bold dark:text-on-surface-dark-strong" aria-current="page">Breadcrumb</li>
18
+ </ol>
19
+ </nav>
20
+ </template>
@@ -0,0 +1,10 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). Styles via theme tokens
2
+ (bg-primary / rounded-radius / …) — needs Tailwind + @apex-stack/theme. -->
3
+ <template x-data>
4
+ <button
5
+ type="button"
6
+ class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-radius bg-primary border border-primary px-4 py-2 text-sm font-medium tracking-wide text-on-primary transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary active:opacity-100 active:outline-offset-0 disabled:opacity-75 disabled:cursor-not-allowed dark:bg-primary-dark dark:border-primary-dark dark:text-on-primary-dark dark:focus-visible:outline-primary-dark"
7
+ >
8
+ <slot>Button</slot>
9
+ </button>
10
+ </template>
@@ -0,0 +1,6 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template>
3
+ <div class="flex flex-col gap-4 rounded-radius border border-outline bg-surface-alt p-6 text-on-surface dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark">
4
+ <slot></slot>
5
+ </div>
6
+ </template>
@@ -0,0 +1,12 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template>
3
+ <label for="checkboxDefault" class="flex items-center gap-2 text-sm font-medium text-on-surface dark:text-on-surface-dark has-checked:text-on-surface-strong dark:has-checked:text-on-surface-dark-strong has-disabled:cursor-not-allowed has-disabled:opacity-75">
4
+ <span class="relative flex items-center">
5
+ <input id="checkboxDefault" type="checkbox" class="before:content[''] peer relative size-4 appearance-none overflow-hidden rounded-sm border border-outline bg-surface-alt before:absolute before:inset-0 checked:border-primary checked:before:bg-primary focus:outline-2 focus:outline-offset-2 focus:outline-outline-strong checked:focus:outline-primary active:outline-offset-0 disabled:cursor-not-allowed dark:border-outline-dark dark:bg-surface-dark-alt dark:checked:border-primary-dark dark:checked:before:bg-primary-dark dark:focus:outline-outline-dark-strong dark:checked:focus:outline-primary-dark" />
6
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" stroke="currentColor" fill="none" stroke-width="4" class="pointer-events-none invisible absolute left-1/2 top-1/2 size-3 -translate-x-1/2 -translate-y-1/2 text-on-primary peer-checked:visible dark:text-on-primary-dark">
7
+ <path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5" />
8
+ </svg>
9
+ </span>
10
+ <span><slot>Notifications</slot></span>
11
+ </label>
12
+ </template>
@@ -0,0 +1,18 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template x-data="{ isOpen: false, openedWithKeyboard: false }">
3
+ <div class="relative w-fit" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false">
4
+ <button type="button" x-on:click="isOpen = ! isOpen" class="inline-flex items-center gap-2 whitespace-nowrap rounded-radius border border-outline bg-surface-alt px-4 py-2 text-sm font-medium tracking-wide transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-outline-strong dark:border-outline-dark dark:bg-surface-dark-alt dark:focus-visible:outline-outline-dark-strong" aria-haspopup="true" x-on:keydown.space.prevent="openedWithKeyboard = true" x-on:keydown.enter.prevent="openedWithKeyboard = true" x-on:keydown.down.prevent="openedWithKeyboard = true" x-bind:class="isOpen || openedWithKeyboard ? 'text-on-surface-strong dark:text-on-surface-dark-strong' : 'text-on-surface dark:text-on-surface-dark'" x-bind:aria-expanded="isOpen || openedWithKeyboard">
5
+ Actions Menu
6
+ <svg aria-hidden="true" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="size-4 rotate-0">
7
+ <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"/>
8
+ </svg>
9
+ </button>
10
+ <div x-cloak x-show="isOpen || openedWithKeyboard" x-transition x-trap="openedWithKeyboard" x-on:click.outside="isOpen = false, openedWithKeyboard = false" x-on:keydown.down.prevent="$focus.wrap().next()" x-on:keydown.up.prevent="$focus.wrap().previous()" class="absolute top-11 left-0 flex w-fit min-w-48 flex-col overflow-hidden rounded-radius border border-outline bg-surface-alt dark:border-outline-dark dark:bg-surface-dark-alt" role="menu">
11
+ <slot>
12
+ <a href="#" class="bg-surface-alt px-4 py-2 text-sm text-on-surface hover:bg-surface-dark-alt/5 hover:text-on-surface-strong focus-visible:bg-surface-dark-alt/10 focus-visible:text-on-surface-strong focus-visible:outline-hidden dark:bg-surface-dark-alt dark:text-on-surface-dark dark:hover:bg-surface-alt/5 dark:hover:text-on-surface-dark-strong dark:focus-visible:bg-surface-alt/10 dark:focus-visible:text-on-surface-dark-strong" role="menuitem">Dashboard</a>
13
+ <a href="#" class="bg-surface-alt px-4 py-2 text-sm text-on-surface hover:bg-surface-dark-alt/5 hover:text-on-surface-strong focus-visible:bg-surface-dark-alt/10 focus-visible:text-on-surface-strong focus-visible:outline-hidden dark:bg-surface-dark-alt dark:text-on-surface-dark dark:hover:bg-surface-alt/5 dark:hover:text-on-surface-dark-strong dark:focus-visible:bg-surface-alt/10 dark:focus-visible:text-on-surface-dark-strong" role="menuitem">Settings</a>
14
+ <a href="#" class="bg-surface-alt px-4 py-2 text-sm text-on-surface hover:bg-surface-dark-alt/5 hover:text-on-surface-strong focus-visible:bg-surface-dark-alt/10 focus-visible:text-on-surface-strong focus-visible:outline-hidden dark:bg-surface-dark-alt dark:text-on-surface-dark dark:hover:bg-surface-alt/5 dark:hover:text-on-surface-dark-strong dark:focus-visible:bg-surface-alt/10 dark:focus-visible:text-on-surface-dark-strong" role="menuitem">Sign Out</a>
15
+ </slot>
16
+ </div>
17
+ </div>
18
+ </template>
@@ -0,0 +1,4 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template>
3
+ <kbd class="inline-block size-min whitespace-nowrap rounded-md border border-outline bg-surface-alt px-2 py-1 font-mono text-xs font-semibold text-on-surface dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark"><slot>Ctrl</slot></kbd>
4
+ </template>
@@ -0,0 +1,4 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template>
3
+ <a href="#" class="font-medium text-primary underline-offset-2 hover:underline focus:underline focus:outline-hidden dark:text-primary-dark"><slot>Read if bored</slot></a>
4
+ </template>
@@ -0,0 +1,25 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template x-data="{ modalIsOpen: false }">
3
+ <div>
4
+ <button x-on:click="modalIsOpen = true" type="button" class="whitespace-nowrap rounded-radius border border-primary dark:border-primary-dark bg-primary px-4 py-2 text-center text-sm font-medium tracking-wide text-on-primary transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary active:opacity-100 active:outline-offset-0 dark:bg-primary-dark dark:text-on-primary-dark dark:focus-visible:outline-primary-dark">Open Modal</button>
5
+ <div x-cloak x-show="modalIsOpen" x-transition.opacity.duration.200ms x-trap.inert.noscroll="modalIsOpen" x-on:keydown.esc.window="modalIsOpen = false" x-on:click.self="modalIsOpen = false" class="fixed inset-0 z-30 flex items-end justify-center bg-black/20 p-4 pb-8 backdrop-blur-md sm:items-center lg:p-8" role="dialog" aria-modal="true" aria-labelledby="modalTitle">
6
+ <div x-show="modalIsOpen" x-transition:enter="transition ease-out duration-200 delay-100 motion-reduce:transition-opacity" x-transition:enter-start="opacity-0 scale-50" x-transition:enter-end="opacity-100 scale-100" class="flex max-w-lg flex-col gap-4 overflow-hidden rounded-radius border border-outline bg-surface text-on-surface dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark">
7
+ <div class="flex items-center justify-between border-b border-outline bg-surface-alt/60 p-4 dark:border-outline-dark dark:bg-surface-dark/20">
8
+ <h3 id="modalTitle" class="font-semibold tracking-wide text-on-surface-strong dark:text-on-surface-dark-strong"><slot name="title">Modal Title</slot></h3>
9
+ <button x-on:click="modalIsOpen = false" aria-label="close modal">
10
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" stroke="currentColor" fill="none" stroke-width="1.4" class="w-5 h-5">
11
+ <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
12
+ </svg>
13
+ </button>
14
+ </div>
15
+ <div class="px-4 py-8">
16
+ <slot>Modal body content.</slot>
17
+ </div>
18
+ <div class="flex flex-col-reverse justify-between gap-2 border-t border-outline bg-surface-alt/60 p-4 dark:border-outline-dark dark:bg-surface-dark/20 sm:flex-row sm:items-center md:justify-end">
19
+ <button x-on:click="modalIsOpen = false" type="button" class="whitespace-nowrap rounded-radius px-4 py-2 text-center text-sm font-medium tracking-wide text-on-surface transition hover:opacity-75 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary active:opacity-100 active:outline-offset-0 dark:text-on-surface-dark dark:focus-visible:outline-primary-dark">Cancel</button>
20
+ <button x-on:click="modalIsOpen = false" type="button" class="whitespace-nowrap rounded-radius bg-primary border border-primary dark:border-primary-dark px-4 py-2 text-center text-sm font-medium tracking-wide text-on-primary transition hover:opacity-75 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary active:opacity-100 active:outline-offset-0 dark:bg-primary-dark dark:text-on-primary-dark dark:focus-visible:outline-primary-dark">Confirm</button>
21
+ </div>
22
+ </div>
23
+ </div>
24
+ </div>
25
+ </template>
@@ -0,0 +1,6 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template x-data="{ currentVal: 20 ,minVal: 0 ,maxVal: 100, calcPercentage(min, max, val){return ((val-min)/(max-min))*100} }">
3
+ <div class="flex h-2.5 w-full overflow-hidden rounded-radius bg-surface-alt dark:bg-surface-dark-alt" role="progressbar" aria-label="default progress bar" x-bind:aria-valuenow="currentVal" x-bind:aria-valuemin="minVal" x-bind:aria-valuemax="maxVal">
4
+ <div class="h-full rounded-radius bg-primary dark:bg-primary-dark" x-bind:style="`width: ${calcPercentage(minVal, maxVal, currentVal)}%`"></div>
5
+ </div>
6
+ </template>
@@ -0,0 +1,7 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template>
3
+ <div class="flex items-center justify-start gap-2 font-medium text-on-surface has-disabled:opacity-75 dark:text-on-surface-dark">
4
+ <input id="radioDefault" type="radio" class="before:content[''] relative h-4 w-4 appearance-none rounded-full border border-outline bg-surface-alt before:invisible before:absolute before:left-1/2 before:top-1/2 before:h-1.5 before:w-1.5 before:-translate-x-1/2 before:-translate-y-1/2 before:rounded-full before:bg-on-primary checked:border-primary checked:bg-primary checked:before:visible focus:outline-2 focus:outline-offset-2 focus:outline-outline-strong checked:focus:outline-primary disabled:cursor-not-allowed dark:border-outline-dark dark:bg-surface-dark-alt dark:before:bg-on-primary-dark dark:checked:border-primary-dark dark:checked:bg-primary-dark dark:focus:outline-outline-dark-strong dark:checked:focus:outline-primary-dark" name="radioDefault" value="" />
5
+ <label for="radioDefault" class="text-sm"><slot>Option</slot></label>
6
+ </div>
7
+ </template>
@@ -0,0 +1,15 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template>
3
+ <div class="relative flex w-full max-w-xs flex-col gap-1 text-on-surface dark:text-on-surface-dark">
4
+ <label for="os" class="w-fit pl-0.5 text-sm"><slot>Operating System</slot></label>
5
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="absolute pointer-events-none right-4 top-8 size-5">
6
+ <path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" />
7
+ </svg>
8
+ <select id="os" name="os" class="w-full appearance-none rounded-radius border border-outline bg-surface-alt px-4 py-2 text-sm focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary disabled:cursor-not-allowed disabled:opacity-75 dark:border-outline-dark dark:bg-surface-dark-alt/50 dark:focus-visible:outline-primary-dark">
9
+ <option selected>Please Select</option>
10
+ <option value="mac">Mac</option>
11
+ <option value="windows">Windows</option>
12
+ <option value="linux">Linux</option>
13
+ </select>
14
+ </div>
15
+ </template>
@@ -0,0 +1,10 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template>
3
+ <div class="flex w-full flex-col gap-2">
4
+ <div class="h-3.5 w-full animate-pulse rounded-radius bg-on-surface/30 dark:bg-on-surface-dark/30" aria-hidden="true"></div>
5
+ <div class="h-3.5 w-full animate-pulse rounded-radius bg-on-surface/30 dark:bg-on-surface-dark/30" aria-hidden="true"></div>
6
+ <div class="h-3.5 w-full animate-pulse rounded-radius bg-on-surface/30 dark:bg-on-surface-dark/30" aria-hidden="true"></div>
7
+ <div class="h-3.5 w-1/2 animate-pulse rounded-radius bg-on-surface/30 dark:bg-on-surface-dark/30" aria-hidden="true"></div>
8
+ <span class="sr-only">loading</span>
9
+ </div>
10
+ </template>
@@ -0,0 +1,7 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template>
3
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" class="size-5 fill-on-surface motion-safe:animate-spin dark:fill-on-surface-dark">
4
+ <path d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z" opacity=".25" />
5
+ <path d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z" />
6
+ </svg>
7
+ </template>
@@ -0,0 +1,17 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template x-data="{ selectedTab: 'groups' }">
3
+ <div class="w-full">
4
+ <div x-on:keydown.right.prevent="$focus.wrap().next()" x-on:keydown.left.prevent="$focus.wrap().previous()" class="flex gap-2 overflow-x-auto border-b border-outline dark:border-outline-dark" role="tablist" aria-label="tab options">
5
+ <button x-on:click="selectedTab = 'groups'" x-bind:aria-selected="selectedTab === 'groups'" x-bind:tabindex="selectedTab === 'groups' ? '0' : '-1'" x-bind:class="selectedTab === 'groups' ? 'font-bold text-primary border-b-2 border-primary dark:border-primary-dark dark:text-primary-dark' : 'text-on-surface font-medium dark:text-on-surface-dark dark:hover:border-b-outline-dark-strong dark:hover:text-on-surface-dark-strong hover:border-b-2 hover:border-b-outline-strong hover:text-on-surface-strong'" class="h-min px-4 py-2 text-sm" type="button" role="tab" aria-controls="tabpanelGroups">Groups</button>
6
+ <button x-on:click="selectedTab = 'likes'" x-bind:aria-selected="selectedTab === 'likes'" x-bind:tabindex="selectedTab === 'likes' ? '0' : '-1'" x-bind:class="selectedTab === 'likes' ? 'font-bold text-primary border-b-2 border-primary dark:border-primary-dark dark:text-primary-dark' : 'text-on-surface font-medium dark:text-on-surface-dark dark:hover:border-b-outline-dark-strong dark:hover:text-on-surface-dark-strong hover:border-b-2 hover:border-b-outline-strong hover:text-on-surface-strong'" class="h-min px-4 py-2 text-sm" type="button" role="tab" aria-controls="tabpanelLikes">Likes</button>
7
+ <button x-on:click="selectedTab = 'comments'" x-bind:aria-selected="selectedTab === 'comments'" x-bind:tabindex="selectedTab === 'comments' ? '0' : '-1'" x-bind:class="selectedTab === 'comments' ? 'font-bold text-primary border-b-2 border-primary dark:border-primary-dark dark:text-primary-dark' : 'text-on-surface font-medium dark:text-on-surface-dark dark:hover:border-b-outline-dark-strong dark:hover:text-on-surface-dark-strong hover:border-b-2 hover:border-b-outline-strong hover:text-on-surface-strong'" class="h-min px-4 py-2 text-sm" type="button" role="tab" aria-controls="tabpanelComments">Comments</button>
8
+ <button x-on:click="selectedTab = 'saved'" x-bind:aria-selected="selectedTab === 'saved'" x-bind:tabindex="selectedTab === 'saved' ? '0' : '-1'" x-bind:class="selectedTab === 'saved' ? 'font-bold text-primary border-b-2 border-primary dark:border-primary-dark dark:text-primary-dark' : 'text-on-surface font-medium dark:text-on-surface-dark dark:hover:border-b-outline-dark-strong dark:hover:text-on-surface-dark-strong hover:border-b-2 hover:border-b-outline-strong hover:text-on-surface-strong'" class="h-min px-4 py-2 text-sm" type="button" role="tab" aria-controls="tabpanelSaved">Saved</button>
9
+ </div>
10
+ <div class="px-2 py-4 text-on-surface dark:text-on-surface-dark">
11
+ <div x-cloak x-show="selectedTab === 'groups'" id="tabpanelGroups" role="tabpanel" aria-label="groups"><slot name="groups">Groups tab content</slot></div>
12
+ <div x-cloak x-show="selectedTab === 'likes'" id="tabpanelLikes" role="tabpanel" aria-label="likes"><slot name="likes">Likes tab content</slot></div>
13
+ <div x-cloak x-show="selectedTab === 'comments'" id="tabpanelComments" role="tabpanel" aria-label="comments"><slot name="comments">Comments tab content</slot></div>
14
+ <div x-cloak x-show="selectedTab === 'saved'" id="tabpanelSaved" role="tabpanel" aria-label="saved"><slot name="saved">Saved tab content</slot></div>
15
+ </div>
16
+ </div>
17
+ </template>
@@ -0,0 +1,7 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template>
3
+ <div class="flex w-full max-w-xs flex-col gap-1 text-on-surface dark:text-on-surface-dark">
4
+ <label for="textInputDefault" class="w-fit pl-0.5 text-sm"><slot>Name</slot></label>
5
+ <input id="textInputDefault" type="text" class="w-full rounded-radius border border-outline bg-surface-alt px-2 py-2 text-sm focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary disabled:cursor-not-allowed disabled:opacity-75 dark:border-outline-dark dark:bg-surface-dark-alt/50 dark:focus-visible:outline-primary-dark" name="name" placeholder="Enter your name" autocomplete="name" />
6
+ </div>
7
+ </template>
@@ -0,0 +1,7 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template>
3
+ <div class="flex w-full max-w-md flex-col gap-1 text-on-surface dark:text-on-surface-dark">
4
+ <label for="textArea" class="w-fit pl-0.5 text-sm"><slot>Comment</slot></label>
5
+ <textarea id="textArea" class="w-full rounded-radius border border-outline bg-surface-alt px-2.5 py-2 text-sm focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary disabled:cursor-not-allowed disabled:opacity-75 dark:border-outline-dark dark:bg-surface-dark-alt/50 dark:focus-visible:outline-primary-dark" rows="3" placeholder="We'd love to hear from you..."></textarea>
6
+ </div>
7
+ </template>
@@ -0,0 +1,8 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template x-data="{ on: true }">
3
+ <label for="apexToggle" class="inline-flex items-center gap-3">
4
+ <input id="apexToggle" type="checkbox" class="peer sr-only" role="switch" x-model="on" />
5
+ <span class="trancking-wide text-sm font-medium text-on-surface peer-checked:text-on-surface-strong peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:text-on-surface-dark dark:peer-checked:text-on-surface-dark-strong"><slot>Toggle</slot></span>
6
+ <div class="relative h-6 w-11 after:h-5 after:w-5 peer-checked:after:translate-x-5 rounded-full border border-outline bg-surface-alt after:absolute after:bottom-0 after:left-[0.0625rem] after:top-0 after:my-auto after:rounded-full after:bg-on-surface after:transition-all after:content-[''] peer-checked:bg-primary peer-checked:after:bg-on-primary peer-focus:outline-2 peer-focus:outline-offset-2 peer-focus:outline-outline-strong peer-focus:peer-checked:outline-primary peer-active:outline-offset-0 peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:border-outline-dark dark:bg-surface-dark-alt dark:after:bg-on-surface-dark dark:peer-checked:bg-primary-dark dark:peer-checked:after:bg-on-primary-dark dark:peer-focus:outline-outline-dark-strong dark:peer-focus:peer-checked:outline-primary-dark" aria-hidden="true"></div>
7
+ </label>
8
+ </template>
@@ -0,0 +1,7 @@
1
+ <!-- Apex component adapted from Penguin UI (MIT). -->
2
+ <template x-data>
3
+ <div class="relative w-fit">
4
+ <button type="button" class="peer rounded-radius bg-surface-alt border border-surface-alt px-4 py-2 font-medium tracking-wide text-on-surface focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary dark:bg-surface-dark-alt dark:border-surface-dark-alt dark:text-on-surface-dark dark:focus-visible:outline-primary-dark" aria-describedby="tooltipContent">Hover Me</button>
5
+ <div id="tooltipContent" class="absolute -top-9 left-1/2 -translate-x-1/2 z-10 whitespace-nowrap rounded-sm bg-surface-dark px-2 py-1 text-center text-sm text-on-surface-dark-strong opacity-0 transition-all ease-out peer-hover:opacity-100 peer-focus:opacity-100 dark:bg-surface dark:text-on-surface-strong" role="tooltip"><slot>Tooltip top</slot></div>
6
+ </div>
7
+ </template>
@@ -0,0 +1,113 @@
1
+ {
2
+ "button": {
3
+ "name": "Button",
4
+ "file": "Button.alpine",
5
+ "description": "Primary button; edit classes for secondary/outline/ghost."
6
+ },
7
+ "card": {
8
+ "name": "Card",
9
+ "file": "Card.alpine",
10
+ "description": "A surface container that inherits the theme."
11
+ },
12
+ "badge": { "name": "Badge", "file": "Badge.alpine", "description": "A small status/label pill." },
13
+ "alert": {
14
+ "name": "Alert",
15
+ "file": "Alert.alpine",
16
+ "description": "Dismissible info alert with icon, message slot, and close button."
17
+ },
18
+ "avatar": {
19
+ "name": "Avatar",
20
+ "file": "Avatar.alpine",
21
+ "description": "Circular initials avatar; content via slot."
22
+ },
23
+ "banner": {
24
+ "name": "Banner",
25
+ "file": "Banner.alpine",
26
+ "description": "Full-width dismissible top banner with a text slot."
27
+ },
28
+ "breadcrumbs": {
29
+ "name": "Breadcrumbs",
30
+ "file": "Breadcrumbs.alpine",
31
+ "description": "Chevron-separated breadcrumb navigation."
32
+ },
33
+ "kbd": {
34
+ "name": "Kbd",
35
+ "file": "Kbd.alpine",
36
+ "description": "Keyboard key indicator; key label via slot."
37
+ },
38
+ "checkbox": {
39
+ "name": "Checkbox",
40
+ "file": "Checkbox.alpine",
41
+ "description": "Checkbox with label and animated check."
42
+ },
43
+ "radio": {
44
+ "name": "Radio",
45
+ "file": "Radio.alpine",
46
+ "description": "Radio button with slotted label."
47
+ },
48
+ "select": {
49
+ "name": "Select",
50
+ "file": "Select.alpine",
51
+ "description": "Native select with floating label + chevron."
52
+ },
53
+ "text-input": {
54
+ "name": "TextInput",
55
+ "file": "TextInput.alpine",
56
+ "description": "Labeled text input with focus/disabled states."
57
+ },
58
+ "textarea": {
59
+ "name": "Textarea",
60
+ "file": "Textarea.alpine",
61
+ "description": "Labeled textarea with focus/disabled states."
62
+ },
63
+ "toggle": {
64
+ "name": "Toggle",
65
+ "file": "Toggle.alpine",
66
+ "description": "Accessible on/off switch with animated knob."
67
+ },
68
+ "spinner": {
69
+ "name": "Spinner",
70
+ "file": "Spinner.alpine",
71
+ "description": "SVG loading spinner (motion-safe)."
72
+ },
73
+ "progress": {
74
+ "name": "Progress",
75
+ "file": "Progress.alpine",
76
+ "description": "Progress bar with ARIA value bindings."
77
+ },
78
+ "skeleton": {
79
+ "name": "Skeleton",
80
+ "file": "Skeleton.alpine",
81
+ "description": "Pulsing placeholder skeleton loader."
82
+ },
83
+ "link": {
84
+ "name": "Link",
85
+ "file": "Link.alpine",
86
+ "description": "Styled inline anchor with slotted label."
87
+ },
88
+ "tooltip": {
89
+ "name": "Tooltip",
90
+ "file": "Tooltip.alpine",
91
+ "description": "Hover/focus tooltip; content via slot."
92
+ },
93
+ "tabs": {
94
+ "name": "Tabs",
95
+ "file": "Tabs.alpine",
96
+ "description": "Tablist with arrow-key focus + panel slots."
97
+ },
98
+ "accordion": {
99
+ "name": "Accordion",
100
+ "file": "Accordion.alpine",
101
+ "description": "Collapsible item with title + body slots."
102
+ },
103
+ "modal": {
104
+ "name": "Modal",
105
+ "file": "Modal.alpine",
106
+ "description": "Dialog with focus-trap, esc/click-outside close."
107
+ },
108
+ "dropdown": {
109
+ "name": "Dropdown",
110
+ "file": "Dropdown.alpine",
111
+ "description": "Menu with keyboard nav + click-outside close."
112
+ }
113
+ }
@@ -0,0 +1,81 @@
1
+ import {
2
+ banner,
3
+ color
4
+ } from "./chunk-QIXJSQLW.js";
5
+
6
+ // src/commands/add.ts
7
+ import { cpSync, existsSync, mkdirSync, readFileSync } from "fs";
8
+ import { dirname, join, resolve } from "path";
9
+ import { fileURLToPath } from "url";
10
+ import { defineCommand } from "citty";
11
+ var REGISTRY_DIR = fileURLToPath(new URL("../components", import.meta.url));
12
+ function loadRegistry() {
13
+ const p = join(REGISTRY_DIR, "registry.json");
14
+ if (!existsSync(p)) return {};
15
+ try {
16
+ return JSON.parse(readFileSync(p, "utf8"));
17
+ } catch {
18
+ return {};
19
+ }
20
+ }
21
+ var addCommand = defineCommand({
22
+ meta: {
23
+ name: "add",
24
+ description: "Add a themeable component into your project (copies the .alpine source)"
25
+ },
26
+ args: {
27
+ name: { type: "positional", required: false, description: "Component to add (e.g. button)" },
28
+ root: { type: "string", description: "Project root", default: "." },
29
+ force: { type: "boolean", default: false, description: "Overwrite an existing component file" }
30
+ },
31
+ run({ args }) {
32
+ const registry = loadRegistry();
33
+ const log = console.log;
34
+ process.stdout.write(banner());
35
+ const keys = Object.keys(registry);
36
+ if (!keys.length) {
37
+ console.error(` ${color.red("\u2717")} No component registry bundled in this build.
38
+ `);
39
+ process.exit(1);
40
+ }
41
+ const name = args.name ? String(args.name).toLowerCase() : "";
42
+ if (!name) {
43
+ log(` ${color.bold("Available components")} ${color.gray("\u2014 apex add <name>")}
44
+ `);
45
+ for (const k of keys) {
46
+ log(` ${color.cyan(k.padEnd(12))} ${color.gray(registry[k]?.description ?? "")}`);
47
+ }
48
+ log("");
49
+ return;
50
+ }
51
+ const entry = registry[name];
52
+ if (!entry) {
53
+ console.error(
54
+ ` ${color.red("\u2717")} Unknown component "${name}". Available: ${keys.join(", ")}
55
+ `
56
+ );
57
+ process.exit(1);
58
+ }
59
+ const root = resolve(process.cwd(), String(args.root));
60
+ const dest = join(root, "components", `${entry.name}.alpine`);
61
+ if (existsSync(dest) && !args.force) {
62
+ log(
63
+ ` ${color.gray("\u2022")} ${entry.name} already exists at components/${entry.name}.alpine ${color.gray("(use --force to overwrite)")}
64
+ `
65
+ );
66
+ return;
67
+ }
68
+ mkdirSync(dirname(dest), { recursive: true });
69
+ cpSync(join(REGISTRY_DIR, entry.file), dest);
70
+ log(` ${color.green("+")} Added ${color.bold(`components/${entry.name}.alpine`)}`);
71
+ log(`
72
+ Use it: ${color.cyan(`<${entry.name} />`)} ${color.gray("in any page/component.")}`);
73
+ log(
74
+ ` ${color.gray("It inherits your theme via --ax-* tokens (see")} ${color.cyan("@apex-stack/theme")}${color.gray(").")}
75
+ `
76
+ );
77
+ }
78
+ });
79
+ export {
80
+ addCommand
81
+ };
package/dist/cli.js CHANGED
@@ -133,6 +133,7 @@ var COMMANDS = [
133
133
  ["build", "Build for production (static, islands, or server)"],
134
134
  ["start", "Run a production server build"],
135
135
  ["make", "Generate a page, component, route, store, middleware\u2026"],
136
+ ["add", "Add a themeable component (button, card, badge\u2026)"],
136
137
  ["upgrade", "Adopt new scaffold defaults (non-destructive)"],
137
138
  ["migrate", "Apply pending database migrations"],
138
139
  ["mcp", "Inspect the MCP server \u2014 list or call tools"]
@@ -149,6 +150,7 @@ var main = defineCommand2({
149
150
  build: () => import("./build-VHS6KZBK.js").then((m) => m.buildCommand),
150
151
  start: () => import("./start-3O3E43PT.js").then((m) => m.startCommand),
151
152
  make: () => import("./make-VAYO5GWA.js").then((m) => m.makeCommand),
153
+ add: () => import("./add-2SBHPLEH.js").then((m) => m.addCommand),
152
154
  upgrade: () => import("./upgrade-WC5F5FKY.js").then((m) => m.upgradeCommand),
153
155
  migrate: () => import("./migrate-X6LIHMIE.js").then((m) => m.migrateCommand),
154
156
  mcp: () => import("./mcp-CH7L4GF3.js").then((m) => m.mcpCommand)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apex-stack/core",
3
- "version": "0.2.2",
3
+ "version": "0.3.1",
4
4
  "description": "The full-stack meta-framework for Alpine.js — CLI and runtime",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -38,7 +38,8 @@
38
38
  "files": [
39
39
  "dist",
40
40
  "templates",
41
- "vscode"
41
+ "vscode",
42
+ "components"
42
43
  ],
43
44
  "dependencies": {
44
45
  "@modelcontextprotocol/sdk": "^1.29.0",