diff --git a/resources/views/components/app/context-nav-item.blade.php b/resources/views/components/app/context-nav-item.blade.php
new file mode 100644
index 0000000..12abe83
--- /dev/null
+++ b/resources/views/components/app/context-nav-item.blade.php
@@ -0,0 +1,20 @@
+@props([
+ 'href',
+ 'label',
+ 'icon',
+ 'current' => false,
+])
+
+ $current,
+ 'text-muted hover:bg-main hover:text-copy' => ! $current,
+ ])
+>
+
+
+
+ {{ $label }}
+
diff --git a/resources/views/components/app/toolbar-item.blade.php b/resources/views/components/app/toolbar-item.blade.php
new file mode 100644
index 0000000..1fc1f1e
--- /dev/null
+++ b/resources/views/components/app/toolbar-item.blade.php
@@ -0,0 +1,20 @@
+@props([
+ 'href',
+ 'label',
+ 'icon',
+ 'current' => false,
+])
+
+ $current,
+ 'text-toolbar-muted hover:bg-white/8 hover:text-white' => ! $current,
+ ])
+>
+
+
+
+ {{ $label }}
+
diff --git a/resources/views/components/layouts/app-shell.blade.php b/resources/views/components/layouts/app-shell.blade.php
index 304e1c6..7d39009 100644
--- a/resources/views/components/layouts/app-shell.blade.php
+++ b/resources/views/components/layouts/app-shell.blade.php
@@ -83,19 +83,12 @@ class="flex size-10 items-center justify-center rounded-lg bg-white/10 text-whit
@foreach ($toolbarItems as $item)
- $item['current'] ?? false,
- 'text-toolbar-muted hover:bg-white/8 hover:text-white' => ! ($item['current'] ?? false),
- ])
- >
-
-
-
- {{ $item['label'] }}
-
+
@endforeach
@@ -106,11 +99,11 @@ class="flex size-10 items-center justify-center rounded-lg bg-white/10 text-whit
class="bg-panel px-5 py-5 lg:w-72 lg:px-6 lg:py-8"
>
-
+
-
Current app
+
Current app
Ideas
@@ -118,19 +111,12 @@ class="bg-panel px-5 py-5 lg:w-72 lg:px-6 lg:py-8"
diff --git a/resources/views/components/ui/avatar.blade.php b/resources/views/components/ui/avatar.blade.php
new file mode 100644
index 0000000..a2ccc77
--- /dev/null
+++ b/resources/views/components/ui/avatar.blade.php
@@ -0,0 +1,17 @@
+@props([
+ 'name' => null,
+ 'initials' => null,
+ 'size' => 'md',
+])
+
+@php
+ $sizeClasses = [
+ 'sm' => 'size-8 text-xs',
+ 'md' => 'size-10 text-sm',
+ 'lg' => 'size-12 text-base',
+ ];
+@endphp
+
+class(['inline-flex items-center justify-center rounded-md bg-accent-soft font-medium text-accent-strong', $sizeClasses[$size] ?? $sizeClasses['md']]) }}>
+ {{ $initials ?? \Illuminate\Support\Str::of((string) $name)->trim()->explode(' ')->filter()->take(2)->map(fn (string $part): string => mb_strtoupper(mb_substr($part, 0, 1)))->implode('') ?: '?' }}
+
diff --git a/resources/views/components/ui/badge.blade.php b/resources/views/components/ui/badge.blade.php
new file mode 100644
index 0000000..0f51ef1
--- /dev/null
+++ b/resources/views/components/ui/badge.blade.php
@@ -0,0 +1,15 @@
+@props([
+ 'tone' => 'neutral',
+])
+
+@php
+ $toneClasses = [
+ 'neutral' => 'bg-main text-muted',
+ 'accent' => 'bg-accent-soft text-accent-strong',
+ 'success' => 'bg-emerald-500/12 text-emerald-700 dark:text-emerald-300',
+ ];
+@endphp
+
+class(['inline-flex items-center rounded-md px-2 py-1 text-xs font-medium', $toneClasses[$tone] ?? $toneClasses['neutral']]) }}>
+ {{ $slot }}
+
diff --git a/resources/views/components/ui/button.blade.php b/resources/views/components/ui/button.blade.php
new file mode 100644
index 0000000..a55b908
--- /dev/null
+++ b/resources/views/components/ui/button.blade.php
@@ -0,0 +1,21 @@
+@props([
+ 'type' => 'button',
+ 'variant' => 'primary',
+])
+
+@php
+ $variantClasses = [
+ 'primary' => 'bg-accent text-white hover:bg-accent-strong',
+ 'secondary' => 'bg-main text-copy hover:bg-panel',
+ 'ghost' => 'bg-transparent text-copy hover:bg-main',
+ ];
+
+ $baseClasses = 'inline-flex items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50';
+@endphp
+
+class([$baseClasses, $variantClasses[$variant] ?? $variantClasses['primary']]) }}
+>
+ {{ $slot }}
+
diff --git a/resources/views/components/ui/dropdown.blade.php b/resources/views/components/ui/dropdown.blade.php
new file mode 100644
index 0000000..2ef70a1
--- /dev/null
+++ b/resources/views/components/ui/dropdown.blade.php
@@ -0,0 +1,13 @@
+@props([
+ 'label' => 'Menu',
+])
+
+class(['group relative']) }}>
+
+ {{ $label }}
+
+
+
+ {{ $slot }}
+
+
diff --git a/resources/views/components/ui/input.blade.php b/resources/views/components/ui/input.blade.php
new file mode 100644
index 0000000..45279c8
--- /dev/null
+++ b/resources/views/components/ui/input.blade.php
@@ -0,0 +1,20 @@
+@props([
+ 'label' => null,
+ 'error' => null,
+])
+
+@php
+ $fieldClasses = 'block w-full rounded-md border-0 bg-main px-3 py-2 text-sm text-copy shadow-none ring-1 ring-inset ring-transparent transition placeholder:text-muted focus:outline-none focus:ring-2 focus:ring-accent';
+@endphp
+
+
+ @if ($label)
+ {{ $label }}
+ @endif
+
+ class([$fieldClasses]) }}>
+
+ @if ($error)
+ {{ $error }}
+ @endif
+
diff --git a/resources/views/components/ui/modal.blade.php b/resources/views/components/ui/modal.blade.php
new file mode 100644
index 0000000..996920e
--- /dev/null
+++ b/resources/views/components/ui/modal.blade.php
@@ -0,0 +1,13 @@
+@props([
+ 'title' => null,
+])
+
+class(['rounded-md bg-panel p-5']) }}>
+ @if ($title)
+
{{ $title }}
+ @endif
+
+
+ {{ $slot }}
+
+
diff --git a/resources/views/components/ui/page-header.blade.php b/resources/views/components/ui/page-header.blade.php
new file mode 100644
index 0000000..53b75e6
--- /dev/null
+++ b/resources/views/components/ui/page-header.blade.php
@@ -0,0 +1,26 @@
+@props([
+ 'eyebrow' => null,
+ 'title' => null,
+ 'description' => null,
+ 'actions' => null,
+])
+
+class(['space-y-3']) }}>
+ @if ($eyebrow)
+ {{ $eyebrow }}
+ @endif
+
+ @if ($title)
+ {{ $title }}
+ @endif
+
+ @if ($description)
+ {{ $description }}
+ @endif
+
+ @if ($actions)
+
+ {{ $actions }}
+
+ @endif
+
diff --git a/resources/views/components/ui/panel.blade.php b/resources/views/components/ui/panel.blade.php
new file mode 100644
index 0000000..5ef8a91
--- /dev/null
+++ b/resources/views/components/ui/panel.blade.php
@@ -0,0 +1,20 @@
+@props([
+ 'title' => null,
+ 'subtitle' => null,
+])
+
+class(['rounded-md bg-panel px-5 py-5']) }}>
+ @if ($title || $subtitle)
+
+ @if ($title)
+ {{ $title }}
+ @endif
+
+ @if ($subtitle)
+ {{ $subtitle }}
+ @endif
+
+ @endif
+
+ {{ $slot }}
+
diff --git a/resources/views/components/ui/select.blade.php b/resources/views/components/ui/select.blade.php
new file mode 100644
index 0000000..de42686
--- /dev/null
+++ b/resources/views/components/ui/select.blade.php
@@ -0,0 +1,22 @@
+@props([
+ 'label' => null,
+ 'error' => null,
+])
+
+@php
+ $fieldClasses = 'block w-full rounded-md border-0 bg-main px-3 py-2 text-sm text-copy shadow-none ring-1 ring-inset ring-transparent transition focus:outline-none focus:ring-2 focus:ring-accent';
+@endphp
+
+
+ @if ($label)
+ {{ $label }}
+ @endif
+
+ class([$fieldClasses]) }}>
+ {{ $slot }}
+
+
+ @if ($error)
+ {{ $error }}
+ @endif
+
diff --git a/resources/views/components/ui/textarea.blade.php b/resources/views/components/ui/textarea.blade.php
new file mode 100644
index 0000000..eee6879
--- /dev/null
+++ b/resources/views/components/ui/textarea.blade.php
@@ -0,0 +1,20 @@
+@props([
+ 'label' => null,
+ 'error' => null,
+])
+
+@php
+ $fieldClasses = 'block w-full rounded-md border-0 bg-main px-3 py-2 text-sm text-copy shadow-none ring-1 ring-inset ring-transparent transition placeholder:text-muted focus:outline-none focus:ring-2 focus:ring-accent';
+@endphp
+
+
+ @if ($label)
+ {{ $label }}
+ @endif
+
+
+
+ @if ($error)
+ {{ $error }}
+ @endif
+
diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php
index 64df9e8..5c62701 100644
--- a/resources/views/home.blade.php
+++ b/resources/views/home.blade.php
@@ -65,25 +65,22 @@
-
- Workspace
- Turn rough product thoughts into team-ready software proposals.
-
- This shell is the first pass at the shared product frame: app switcher on the left, context navigation beside it,
- and a main work area ready for ideas, planning, development, testing, security, ops, and admin tools.
-
-
+
-
+
Current draft
Private idea workspace
-
+
Draft
-
+
@@ -96,10 +93,12 @@
features can slot in without rewriting the frame.
-
+
-
- Next in this app
+
@@ -115,7 +114,7 @@
Propose a polished draft to the team
-
+
diff --git a/tests/Feature/Ui/UiComponentsTest.php b/tests/Feature/Ui/UiComponentsTest.php
new file mode 100644
index 0000000..062efb7
--- /dev/null
+++ b/tests/Feature/Ui/UiComponentsTest.php
@@ -0,0 +1,37 @@
+Save');
+
+ expect($rendered)->toContain('type="button"');
+ expect($rendered)->toContain('Save');
+});
+
+it('renders the shared form controls', function (): void {
+ $rendered = Blade::render(<<<'BLADE'
+
+
+
+ Draft
+
+ BLADE);
+
+ expect($rendered)->toContain('Name');
+ expect($rendered)->toContain('Summary');
+ expect($rendered)->toContain('State');
+});
+
+it('renders the shared display primitives', function (): void {
+ $rendered = Blade::render('Draft ');
+
+ expect($rendered)->toContain('Draft');
+ expect($rendered)->toContain('TO');
+});
+
+it('renders the shared avatar fallback from a name', function (): void {
+ $rendered = Blade::render(' ');
+
+ expect($rendered)->toContain('TO');
+});