feat(ui): expose app theme contract and public components

This commit is contained in:
2026-07-19 20:23:20 +05:30
parent c5326f7622
commit a3e21163c1
102 changed files with 12748 additions and 600 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ai",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/authz",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/authz — part of the WrNexus framework.",
"license": "MIT",
+11 -11
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/cli",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/cli — part of the WrNexus framework.",
"license": "MIT",
@@ -28,16 +28,16 @@
"wrnexus": "./dist/index.js"
},
"dependencies": {
"@wrnexus/core": "^0.2.61",
"@wrnexus/router": "^0.2.61",
"@wrnexus/csr": "^0.2.61",
"@wrnexus/compiler": "^0.2.61",
"@wrnexus/styles": "^0.2.61",
"@wrnexus/dev-server": "^0.2.61",
"@wrnexus/ui": "^0.2.61",
"@wrnexus/validation": "^0.2.61",
"@wrnexus/i18n": "^0.2.61",
"@wrnexus/db": "^0.2.61"
"@wrnexus/core": "^0.2.62",
"@wrnexus/router": "^0.2.62",
"@wrnexus/csr": "^0.2.62",
"@wrnexus/compiler": "^0.2.62",
"@wrnexus/styles": "^0.2.62",
"@wrnexus/dev-server": "^0.2.62",
"@wrnexus/ui": "^0.2.62",
"@wrnexus/validation": "^0.2.62",
"@wrnexus/i18n": "^0.2.62",
"@wrnexus/db": "^0.2.62"
},
"files": [
"dist"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/compiler",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/compiler — part of the WrNexus framework.",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/core",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/core — part of the WrNexus framework.",
"license": "MIT",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/csr",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/csr — part of the WrNexus framework.",
"license": "MIT",
@@ -21,7 +21,7 @@
}
},
"dependencies": {
"@wrnexus/core": "^0.2.61"
"@wrnexus/core": "^0.2.62"
},
"files": [
"dist"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/db",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/db — part of the WrNexus framework.",
"license": "MIT",
+13 -13
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/dev-server",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/dev-server — part of the WrNexus framework.",
"license": "MIT",
@@ -25,18 +25,18 @@
}
},
"dependencies": {
"@wrnexus/core": "^0.2.61",
"@wrnexus/router": "^0.2.61",
"@wrnexus/ssr": "^0.2.61",
"@wrnexus/csr": "^0.2.61",
"@wrnexus/compiler": "^0.2.61",
"@wrnexus/styles": "^0.2.61",
"@wrnexus/ui": "^0.2.61",
"@wrnexus/validation": "^0.2.61",
"@wrnexus/i18n": "^0.2.61",
"@wrnexus/db": "^0.2.61",
"@wrnexus/pubsub": "^0.2.61",
"@wrnexus/uploader": "^0.2.61"
"@wrnexus/core": "^0.2.62",
"@wrnexus/router": "^0.2.62",
"@wrnexus/ssr": "^0.2.62",
"@wrnexus/csr": "^0.2.62",
"@wrnexus/compiler": "^0.2.62",
"@wrnexus/styles": "^0.2.62",
"@wrnexus/ui": "^0.2.62",
"@wrnexus/validation": "^0.2.62",
"@wrnexus/i18n": "^0.2.62",
"@wrnexus/db": "^0.2.62",
"@wrnexus/pubsub": "^0.2.62",
"@wrnexus/uploader": "^0.2.62"
},
"files": [
"dist"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/encryption",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/encryption — part of the WrNexus framework.",
"license": "MIT",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/helpers",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
"license": "MIT",
@@ -21,7 +21,7 @@
}
},
"dependencies": {
"@wrnexus/core": "^0.2.61"
"@wrnexus/core": "^0.2.62"
},
"files": [
"dist"
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/i18n",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/i18n — part of the WrNexus framework.",
"license": "MIT",
@@ -21,7 +21,7 @@
}
},
"dependencies": {
"@wrnexus/core": "^0.2.61"
"@wrnexus/core": "^0.2.62"
},
"files": [
"dist"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/jwt",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/jwt — part of the WrNexus framework.",
"license": "MIT",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/mobile",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/mobile — part of the WrNexus framework.",
"license": "MIT",
@@ -21,7 +21,7 @@
}
},
"dependencies": {
"@wrnexus/native": "^0.2.61"
"@wrnexus/native": "^0.2.62"
},
"files": [
"dist"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/native",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/native — part of the WrNexus framework.",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/oauth",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/oauth — part of the WrNexus framework.",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/pubsub",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/pubsub — part of the WrNexus framework.",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/queue",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/queue — part of the WrNexus framework.",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/reactive",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/reactive — part of the WrNexus framework.",
"license": "MIT",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/router",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/router — part of the WrNexus framework.",
"license": "MIT",
@@ -21,8 +21,8 @@
}
},
"dependencies": {
"@wrnexus/compiler": "^0.2.61",
"@wrnexus/core": "^0.2.61"
"@wrnexus/compiler": "^0.2.62",
"@wrnexus/core": "^0.2.62"
},
"files": [
"dist"
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ssr",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/ssr — part of the WrNexus framework.",
"license": "MIT",
@@ -21,7 +21,7 @@
}
},
"dependencies": {
"@wrnexus/core": "^0.2.61"
"@wrnexus/core": "^0.2.62"
},
"files": [
"dist"
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/styles",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/styles — part of the WrNexus framework.",
"license": "MIT",
@@ -21,7 +21,7 @@
}
},
"dependencies": {
"@wrnexus/uploader": "^0.2.61"
"@wrnexus/uploader": "^0.2.62"
},
"files": [
"dist"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/test",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/test — part of the WrNexus framework.",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/tracking",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/tracking — part of the WrNexus framework.",
"license": "MIT",
+110 -20
View File
@@ -1,6 +1,6 @@
# WRNexus UI component reference
This reference is generated from the 891 packaged `.wrn` component sources. Props marked required have no default; all others show their runtime default.
This reference is generated from the 901 packaged `.wrn` component sources. Props marked required have no default; all others show their runtime default.
## Actions
@@ -1964,9 +1964,9 @@ Reusable navigation item component.
Reusable newsletter form component.
- Mount: `data-component="NewsletterForm"`
- Props: `eyebrow: string = ""`, `title: string = ""`, `description: string = ""`, `href: string = ""`, `actionLabel: string = "Learn more"`, `image: string = ""`, `alt: string = ""`, `class: string = ""`
- Slots: `default`
- Events: None
- Props: `class: string = ""`, `eyebrow: string = ""`, `title: string = "Stay up to date"`, `description: string = ""`, `placeholder: string = "Enter your email"`, `buttonLabel: string = "Subscribe"`, `action: string = "/newsletter/subscribe"`, `method: string = "post"`, `emailName: string = "email"`, `privacyLabel: string = ""`, `privacyHref: string = "/privacy"`, `successMessage: string = "You are subscribed."`, `errorMessage: string = "Something went wrong. Please try again."`, `iconClass: string = "icon-[lucide--mail]"`, `centered: boolean = false`, `compact: boolean = false`, `showIcon: boolean = true`, `showPrivacy: boolean = true`, `fullWidth: boolean = false`
- Slots: None
- Events: `submit`, `input`
### NotFoundPage
@@ -2911,7 +2911,7 @@ Reusable back to top component.
Reusable breadcrumbs component.
- Mount: `data-component="Breadcrumbs"`
- Props: `class: string = ""`, `homeLabel: string = "Home"`, `homeHref: string = "/"`, `current: string = "Current page"`
- Props: `class: string = ""`, `items: string = []`, `centered: boolean = false`, `compact: boolean = false`, `showHome: boolean = true`, `homeLabel: string = "Home"`, `homeHref: string = "/"`
- Slots: None
- Events: None
@@ -2951,6 +2951,15 @@ Reusable combobox component.
- Slots: None
- Events: None
### ContactSalesBanner
Reusable contact sales banner component.
- Mount: `data-component="ContactSalesBanner"`
- Props: `class: string = ""`, `eyebrow: string = "Enterprise identity"`, `title: string = "Ready to secure your next application?"`, `description: string = ""`, `primaryLabel: string = "Contact sales"`, `primaryHref: string = "/contact"`, `secondaryLabel: string = "Start free"`, `secondaryHref: string = "/sign-up"`, `iconClass: string = "icon-[lucide--messages-square]"`, `centered: boolean = false`, `compact: boolean = false`, `points: string = []`, `trustPoints: string = []`
- Slots: None
- Events: None
### Container
Reusable container component.
@@ -2960,6 +2969,24 @@ Reusable container component.
- Slots: `default`
- Events: None
### CookieConsent
Reusable cookie consent component.
- Mount: `data-component="CookieConsent"`
- Props: `class: string = ""`
- Slots: None
- Events: `click`, `change`
### DevelopersMegaMenu
Reusable developers mega menu component.
- Mount: `data-component="DevelopersMegaMenu"`
- Props: `class: string = ""`
- Slots: None
- Events: None
### disclosure
Reusable disclosure component.
@@ -3109,8 +3136,8 @@ Reusable link component.
Reusable logo cloud component.
- Mount: `data-component="LogoCloud"`
- Props: `class: string = ""`, `title: string = "Trusted by modern teams"`
- Slots: `default`
- Props: `class: string = ""`, `eyebrow: string = ""`, `title: string = "Trusted by teams building modern products"`, `description: string = ""`, `logos: string = []`, `centered: boolean = true`, `compact: boolean = false`, `variant: string = "strip"`, `grayscale: boolean = true`, `showNames: boolean = false`, `maxItems: number = 8`
- Slots: None
- Events: None
### Modal
@@ -3149,6 +3176,15 @@ Reusable notification dot component.
- Slots: `default`
- Events: None
### PageHeader
Reusable page header component.
- Mount: `data-component="PageHeader"`
- Props: `class: string = ""`, `eyebrow: string = ""`, `title: string = ""`, `description: string = ""`, `primaryLabel: string = ""`, `primaryHref: string = ""`, `secondaryLabel: string = ""`, `secondaryHref: string = ""`, `icon: string = "sparkles"`, `centered: boolean = false`, `compact: boolean = false`, `showBreadcrumbs: boolean = false`, `breadcrumbParent: string = ""`, `breadcrumbParentHref: string = ""`, `breadcrumbCurrent: string = ""`, `highlights: string = []`
- Slots: None
- Events: None
### PageHeading
Reusable page heading component.
@@ -3172,7 +3208,7 @@ Reusable page shell component.
Reusable pagination component.
- Mount: `data-component="Pagination"`
- Props: `class: string = ""`, `page: number = 1`, `totalPages: number = 1`, `previousHref: string = "#"`, `nextHref: string = "#"`
- Props: `class: string = ""`, `currentPage: number = 1`, `totalPages: number = 1`, `previousHref: string = ""`, `nextHref: string = ""`, `pages: string = []`, `showNumbers: boolean = true`, `showSummary: boolean = true`, `totalItems: number = 0`, `pageSize: number = 10`, `compact: boolean = false`, `centered: boolean = false`
- Slots: None
- Events: None
@@ -3185,6 +3221,15 @@ Reusable pill component.
- Slots: `default`
- Events: None
### ProductMegaMenu
Reusable product mega menu component.
- Mount: `data-component="ProductMegaMenu"`
- Props: `class: string = ""`
- Slots: None
- Events: None
### progress
Reusable progress component.
@@ -3208,9 +3253,27 @@ Reusable progress bar component.
Reusable public footer component.
- Mount: `data-component="PublicFooter"`
- Props: `class: string = ""`, `brand: string = "WRNexusJS"`, `description: string = "SSR-first, Bun-native full-stack framework."`, `copyright: string = "© 2026 WorkRoot Workspace"`
- Slots: `default`
- Events: None
- Props: `class: string = ""`
- Slots: None
- Events: `submit`
### PublicMobileNavigation
Reusable public mobile navigation component.
- Mount: `data-component="PublicMobileNavigation"`
- Props: `class: string = ""`
- Slots: None
- Events: `click`
### PublicSearch
Reusable public search component.
- Mount: `data-component="PublicSearch"`
- Props: `class: string = ""`, `query: string = ""`, `placeholder: string = "Search..."`, `label: string = "Search"`, `action: string = ""`, `method: string = "get"`, `name: string = "q"`, `buttonLabel: string = ""`, `clearLabel: string = "Clear search"`, `size: string = "default"`, `centered: boolean = false`, `fullWidth: boolean = false`, `showShortcut: boolean = false`, `shortcutLabel: string = "⌘ K"`, `suggestions: string = []`
- Slots: None
- Events: `input`, `focus`, `blur`, `click`
### Radio
@@ -3257,6 +3320,15 @@ Reusable select component.
- Slots: `default`
- Events: None
### SocialShare
Reusable social share component.
- Mount: `data-component="SocialShare"`
- Props: `class: string = ""`, `title: string = "Share this page"`, `description: string = ""`, `url: string = ""`, `shareText: string = ""`, `centered: boolean = false`, `compact: boolean = false`, `showTitle: boolean = true`, `showCopy: boolean = true`, `networks: string = []`
- Slots: None
- Events: `click`
### spacer
Reusable spacer component.
@@ -3284,6 +3356,15 @@ Reusable stack component.
- Slots: `default`
- Events: None
### StatusBanner
Reusable status banner component.
- Mount: `data-component="StatusBanner"`
- Props: `class: string = ""`, `type: string = "info"`, `title: string = ""`, `description: string = ""`, `actionLabel: string = ""`, `actionHref: string = ""`, `dismissible: boolean = true`, `compact: boolean = false`, `details: string = []`
- Slots: None
- Events: `click`
### Sticky
Reusable sticky component.
@@ -3356,6 +3437,15 @@ Reusable toast component.
- Slots: None
- Events: `click`
### ToastHost
Reusable toast host component.
- Mount: `data-component="ToastHost"`
- Props: `class: string = ""`
- Slots: None
- Events: `click`
### Typography
Reusable typography component.
@@ -4292,7 +4382,7 @@ Reusable deprecated feature alert component.
Reusable empty state component.
- Mount: `data-component="EmptyState"`
- Props: `class: string = ""`, `icon: string = ""`, `title: string = "Nothing here yet"`, `description: string = ""`, `actionLabel: string = ""`, `actionHref: string = "#"`
- Props: `class: string = ""`, `eyebrow: string = ""`, `title: string = "Nothing found"`, `description: string = ""`, `iconClass: string = "icon-[lucide--inbox]"`, `primaryLabel: string = ""`, `primaryHref: string = ""`, `secondaryLabel: string = ""`, `secondaryHref: string = ""`, `compact: boolean = false`, `centered: boolean = true`, `suggestions: string = []`
- Slots: None
- Events: None
@@ -4301,9 +4391,9 @@ Reusable empty state component.
Reusable error state component.
- Mount: `data-component="ErrorState"`
- Props: `class: string = ""`, `code: string = "500"`, `title: string = "Something went wrong"`, `description: string = "Please try again."`, `actionLabel: string = "Try again"`, `actionHref: string = "/"`
- Props: `class: string = ""`, `type: string = "error"`, `eyebrow: string = ""`, `title: string = "Something went wrong"`, `description: string = ""`, `errorCode: string = ""`, `iconClass: string = ""`, `primaryLabel: string = ""`, `primaryHref: string = ""`, `secondaryLabel: string = ""`, `secondaryHref: string = ""`, `retryLabel: string = ""`, `retryAction: string = ""`, `centered: boolean = true`, `compact: boolean = false`, `details: string = []`
- Slots: None
- Events: None
- Events: `click`
### FeatureUnavailableState
@@ -7113,8 +7203,8 @@ Reusable product page shell component.
Reusable public header component.
- Mount: `data-component="PublicHeader"`
- Props: `class: string = ""`, `brand: string = "WRNexusJS"`, `homeHref: string = "/"`, `signInHref: string = "/signin"`, `ctaHref: string = "/start"`, `ctaLabel: string = "Get started"`
- Slots: `default`, `mobile`
- Props: `class: string = ""`
- Slots: None
- Events: `click`
### PublicPageShell
@@ -7547,8 +7637,8 @@ Reusable prompt dialog component.
Reusable resources mega menu component.
- Mount: `data-component="ResourcesMegaMenu"`
- Props: `title: string = ""`, `description: string = ""`, `open: boolean = false`, `closeLabel: string = "Close"`, `class: string = ""`
- Slots: `default`
- Props: `class: string = ""`
- Slots: None
- Events: None
### SearchDialog
@@ -7583,8 +7673,8 @@ Reusable session expired dialog component.
Reusable solutions mega menu component.
- Mount: `data-component="SolutionsMegaMenu"`
- Props: `title: string = ""`, `description: string = ""`, `open: boolean = false`, `closeLabel: string = "Close"`, `class: string = ""`
- Slots: `default`
- Props: `class: string = ""`
- Slots: None
- Events: None
### StepUpAuthenticationDialog
+30
View File
@@ -133,6 +133,36 @@ entry and interaction motion. Override `--wire-motion-fast`,
`--wire-ease-emphasized` to tune it. Hover lift is limited to precise pointing
devices and `prefers-reduced-motion` is honored automatically.
### Using the selected theme in application UI
The active theme and palette are not limited to `@wrnexus/ui` components. The
framework exposes the resolved values as semantic CSS custom properties, so
pages and custom `.wrn` components can use the same contract:
```css
.account-card {
background: var(--wire-color-surface);
color: var(--wire-color-text);
border: 1px solid var(--wire-color-border);
}
.account-card__action {
background: var(--wire-color-primary);
color: var(--wire-color-primary-contrast);
}
```
Stable no-spacing helper classes are also available: `wire-bg-page`,
`wire-bg-surface`, `wire-bg-surface-2`, `wire-bg-primary`, `wire-bg-secondary`,
`wire-text`, `wire-text-muted`, `wire-text-primary`, `wire-text-success`,
`wire-text-warning`, `wire-text-danger`, and `wire-border`.
Tailwind-authored custom markup can continue using the palette families already
used by packaged components. `indigo-*` and `violet-*` resolve to primary,
`blue-*` to info, `emerald-*`/`green-*` to success, `amber-*` to warning, and
`red-*`/`rose-*` to danger. These aliases live at `:root`, so they work outside
a `[data-component]` boundary too.
## Usage
### Auto-discovery
File diff suppressed because it is too large Load Diff
+103 -9
View File
@@ -1,11 +1,105 @@
component Breadcrumbs {
props {
class = ""
homeLabel = "Home"
homeHref = "/"
current = "Current page"
}
view {
<nav class="{class}" aria-label="Breadcrumb"><ol class="flex flex-wrap items-center gap-2 text-sm"><li><a href="{homeHref}" class="text-slate-500 hover:text-violet-600">{homeLabel}</a></li><li aria-hidden="true" class="text-slate-400">/</li><li aria-current="page" class="font-medium text-slate-900 dark:text-white">{current}</li></ol></nav>
}
props {
class: string = ""
items = []
centered = false
compact = false
showHome = true
homeLabel = "Home"
homeHref = "/"
}
view {
<nav
aria-label="Breadcrumb"
class="w-full {class}"
class:hidden="items.length === 0 && !showHome"
>
<ol
class="flex flex-wrap items-center text-slate-500 dark:text-slate-400"
class:justify-center="centered"
class:gap-1.5="compact"
class:text-xs="compact"
class:gap-2="!compact"
class:text-sm="!compact"
>
<!-- Home -->
<li
class:hidden="!showHome"
class:inline-flex="showHome"
>
<a
href="{homeHref}"
class="group inline-flex items-center gap-1.5 rounded-lg transition hover:text-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--home] h-3.5 w-3.5" aria-hidden="true" ></span>
<span class:hidden="compact">
{homeLabel}
</span>
<span
class="sr-only"
class:hidden="!compact"
>
{homeLabel}
</span>
</a>
</li>
<!-- Separator after home -->
<li
aria-hidden="true"
class="text-slate-300 dark:text-slate-700"
class:hidden="!showHome || items.length === 0"
class:inline-flex="showHome && items.length > 0"
>
<span class="icon-[lucide--chevron-right] h-3.5 w-3.5" ></span>
</li>
<!-- Dynamic breadcrumb items -->
{#each items as item, index}
<li class="inline-flex min-w-0 items-center gap-2">
<!-- Linked item -->
<a
href="{item.href}"
class="max-w-48 truncate rounded-lg transition hover:text-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:hover:text-indigo-300 sm:max-w-64"
class:hidden="item.current || item.href === ''"
class:inline-flex="!item.current && item.href !== ''"
>
{item.label}
</a>
<!-- Current item -->
<span
aria-current="page"
class="max-w-48 truncate font-semibold text-slate-950 dark:text-white sm:max-w-64"
class:hidden="!item.current"
class:inline-flex="item.current"
>
{item.label}
</span>
<!-- Plain unlinked item -->
<span
class="max-w-48 truncate text-slate-600 dark:text-slate-300 sm:max-w-64"
class:hidden="item.current || item.href !== ''"
class:inline-flex="!item.current && item.href === ''"
>
{item.label}
</span>
<!-- Separator -->
<span
aria-hidden="true"
class="inline-flex text-slate-300 dark:text-slate-700"
class:hidden="index === items.length - 1"
>
<span class="icon-[lucide--chevron-right] h-3.5 w-3.5" ></span>
</span>
</li>
{/each}
</ol>
</nav>
}
}
@@ -0,0 +1,195 @@
component ContactSalesBanner {
props {
class: string = ""
eyebrow = "Enterprise identity"
title = "Ready to secure your next application?"
description = ""
primaryLabel = "Contact sales"
primaryHref = "/contact"
secondaryLabel = "Start free"
secondaryHref = "/sign-up"
iconClass = "icon-[lucide--messages-square]"
centered = false
compact = false
points = []
trustPoints = []
}
view {
<section
aria-labelledby="contact-sales-banner-title"
class="px-5 sm:px-8 lg:px-10 xl:px-12 {class}"
class:py-10="compact"
class:sm:py-12="compact"
class:py-14="!compact"
class:sm:py-16="!compact"
class:lg:py-20="!compact"
>
<div
class="relative mx-auto w-full max-w-7xl overflow-hidden rounded-3xl border border-indigo-200/80 bg-linear-to-br from-indigo-50 via-white to-violet-50 shadow-xl shadow-indigo-950/5 dark:border-indigo-500/20 dark:from-indigo-500/10 dark:via-slate-900 dark:to-violet-500/10 dark:shadow-black/20"
>
<!-- Background decoration -->
<div
aria-hidden="true"
class="pointer-events-none absolute inset-0 overflow-hidden"
>
<div class="absolute -right-24 -top-24 h-64 w-64 rounded-full bg-indigo-300/20 blur-3xl dark:bg-indigo-500/10"></div>
<div class="absolute -bottom-28 -left-20 h-64 w-64 rounded-full bg-violet-300/20 blur-3xl dark:bg-violet-500/10"></div>
<div class="absolute inset-x-0 top-0 h-px bg-linear-to-r from-transparent via-indigo-400/50 to-transparent dark:via-indigo-400/20"></div>
</div>
<div
class="relative"
class:px-6="compact"
class:py-7="compact"
class:sm:px-8="compact"
class:px-6="!compact"
class:py-8="!compact"
class:sm:px-9="!compact"
class:sm:py-10="!compact"
class:lg:px-12="!compact"
class:lg:py-12="!compact"
>
<!-- Main content -->
<div
class="gap-8"
class:flex="centered"
class:flex-col="centered"
class:items-center="centered"
class:text-center="centered"
class:mx-auto="centered"
class:max-w-4xl="centered"
class:grid="!centered"
class:lg:grid-cols-[minmax(0,1fr)_auto]="!centered"
class:lg:items-center="!centered"
class:lg:gap-12="!centered"
>
<!-- Copy -->
<div
class="min-w-0"
class:max-w-3xl="centered"
class:max-w-4xl="!centered"
>
<!-- Eyebrow -->
<div
class="flex"
class:hidden="eyebrow === ''"
class:justify-center="centered"
class:justify-start="!centered"
>
<span
class="inline-flex items-center gap-2 rounded-full border border-indigo-200 bg-white/80 px-3 py-1.5 text-xs font-semibold text-indigo-700 shadow-sm backdrop-blur-sm dark:border-indigo-500/20 dark:bg-white/5 dark:text-indigo-300"
>
<span class="{iconClass} h-3.5 w-3.5" aria-hidden="true" ></span>
{eyebrow}
</span>
</div>
<!-- Title -->
<h2
id="contact-sales-banner-title"
class="font-bold tracking-tight text-slate-950 dark:text-white"
class:mt-5="eyebrow !== ''"
class:text-2xl="compact"
class:leading-tight="compact"
class:sm:text-3xl="compact"
class:text-3xl="!compact"
class:leading-tight="!compact"
class:sm:text-4xl="!compact"
>
{title}
</h2>
<!-- Description -->
<p
class="mt-4 text-sm leading-7 text-slate-600 dark:text-slate-400 sm:text-base"
class:hidden="description === ''"
class:mx-auto="centered"
class:max-w-2xl="centered"
class:max-w-3xl="!centered"
>
{description}
</p>
<!-- Dynamic points -->
<div
class="mt-6 flex flex-wrap gap-x-5 gap-y-3"
class:hidden="points.length === 0"
class:justify-center="centered"
class:justify-start="!centered"
>
{#each points.slice(0, 4) as point}
<div class="inline-flex items-center gap-2 text-sm font-medium text-slate-700 dark:text-slate-300">
<span
class="grid h-6 w-6 shrink-0 place-items-center rounded-full bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300"
>
<span class="icon-[lucide--check] h-3.5 w-3.5" aria-hidden="true" ></span>
</span>
{point}
</div>
{/each}
</div>
</div>
<!-- Actions -->
<div
class="flex shrink-0 flex-col gap-3 sm:flex-row"
class:hidden="primaryLabel === '' && secondaryLabel === ''"
class:mt-1="centered"
class:justify-center="centered"
>
<a
href="{secondaryHref}"
class="h-12 items-center justify-center rounded-xl border border-slate-200 bg-white/90 px-5 text-sm font-semibold text-slate-700 shadow-sm backdrop-blur-sm transition hover:-translate-y-0.5 hover:border-indigo-300 hover:bg-white hover:text-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-200 dark:hover:border-indigo-500/30 dark:hover:bg-white/10 dark:hover:text-indigo-300"
class:hidden="secondaryLabel === ''"
class:inline-flex="secondaryLabel !== ''"
>
{secondaryLabel}
</a>
<a
href="{primaryHref}"
class="group h-12 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-5 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900"
class:hidden="primaryLabel === ''"
class:inline-flex="primaryLabel !== ''"
>
{primaryLabel}
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
</div>
</div>
<!-- Dynamic trust strip -->
<div
class="mt-8 border-t border-indigo-200/70 pt-5 dark:border-indigo-500/15"
class:hidden="compact || trustPoints.length === 0"
>
<div
class="flex flex-wrap items-center gap-x-6 gap-y-3"
class:justify-center="centered"
class:justify-start="!centered"
>
{#each trustPoints.slice(0, 4) as trustPoint}
<span class="inline-flex items-center gap-2 text-xs font-medium text-slate-500 dark:text-slate-400">
<span class="{trustPoint.iconClass} h-4 w-4 {trustPoint.iconColorClass}" aria-hidden="true" ></span>
{trustPoint.label}
</span>
{/each}
</div>
</div>
</div>
</div>
</section>
}
}
+372
View File
@@ -0,0 +1,372 @@
component CookieConsent {
props {
class: string = ""
}
state bannerOpen = false
state preferencesOpen = false
state analyticsCookies = false
state personalizationCookies = false
state marketingCookies = false
view {
<div class="{class}" class:hidden="!bannerOpen">
<!-- Cookie consent banner -->
<section
role="dialog"
aria-modal="false"
aria-labelledby="cookie-consent-title"
aria-describedby="cookie-consent-description"
class="fixed inset-x-0 bottom-0 z-[80] p-4 sm:p-5 lg:p-6 {class}"
class:hidden="!bannerOpen || preferencesOpen"
class:block="bannerOpen && !preferencesOpen"
>
<div
class="mx-auto w-full max-w-7xl overflow-hidden rounded-3xl border border-slate-200 bg-white/95 shadow-2xl shadow-slate-950/20 backdrop-blur-xl dark:border-white/10 dark:bg-slate-900/95 dark:shadow-black/40"
>
<div class="grid items-center gap-6 p-5 sm:p-6 lg:grid-cols-[minmax(0,1fr)_auto] lg:p-7">
<!-- Message -->
<div class="flex items-start gap-4">
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-2xl bg-indigo-100 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300"
>
<span class="icon-[lucide--cookie] h-5 w-5" aria-hidden="true" ></span>
</span>
<div class="min-w-0">
<div class="flex flex-wrap items-center gap-2">
<h2
id="cookie-consent-title"
class="text-base font-bold text-slate-950 dark:text-white"
>
Your privacy matters
</h2>
<span
class="inline-flex items-center rounded-full bg-emerald-100 px-2.5 py-1 text-[10px] font-bold uppercase tracking-wide text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300"
>
Privacy first
</span>
</div>
<p
id="cookie-consent-description"
class="mt-2 max-w-3xl text-sm leading-6 text-slate-600 dark:text-slate-400"
>
We use essential cookies to operate WrNexus. With your
permission, we also use analytics and personalization cookies
to improve your experience.
</p>
<div class="mt-3 flex flex-wrap items-center gap-x-4 gap-y-2">
<a
href="/cookies"
class="inline-flex items-center gap-1.5 text-xs font-semibold text-indigo-600 transition hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300"
>
Cookie policy
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5" aria-hidden="true" ></span>
</a>
<a
href="/privacy"
class="inline-flex items-center gap-1.5 text-xs font-semibold text-slate-500 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
Privacy policy
</a>
</div>
</div>
</div>
<!-- Actions -->
<div class="grid gap-2 sm:grid-cols-3 lg:flex lg:items-center">
<button
type="button"
@click=" analyticsCookies = false; personalizationCookies = false; marketingCookies = false; bannerOpen = false "
data-cookie-action="essential"
class="inline-flex h-11 items-center justify-center rounded-xl border border-slate-200 bg-white px-4 text-sm font-semibold text-slate-700 transition hover:border-slate-300 hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-200 dark:hover:border-white/20 dark:hover:bg-white/10 dark:hover:text-white"
>
Essential only
</button>
<button
type="button"
@click="preferencesOpen = true"
class="inline-flex h-11 items-center justify-center gap-2 rounded-xl border border-indigo-200 bg-indigo-50 px-4 text-sm font-semibold text-indigo-700 transition hover:border-indigo-300 hover:bg-indigo-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-indigo-500/20 dark:bg-indigo-500/10 dark:text-indigo-300 dark:hover:border-indigo-500/30 dark:hover:bg-indigo-500/15"
>
<span class="icon-[lucide--settings-2] h-4 w-4" aria-hidden="true" ></span>
Preferences
</button>
<button
type="button"
@click=" analyticsCookies = true; personalizationCookies = true; marketingCookies = true; bannerOpen = false "
data-cookie-action="accept-all"
class="inline-flex h-11 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-5 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900"
>
Accept all
<span class="icon-[lucide--check] h-4 w-4" aria-hidden="true" ></span>
</button>
</div>
</div>
</div>
</section>
<!-- Preferences backdrop -->
<button type="button" @click="preferencesOpen = false" aria-label="Close cookie preferences" class="fixed inset-0 z-[90] bg-slate-950/50 backdrop-blur-sm" class:hidden="!preferencesOpen" class:block="preferencesOpen" ></button>
<!-- Preferences modal -->
<section
role="dialog"
aria-modal="true"
aria-labelledby="cookie-preferences-title"
aria-describedby="cookie-preferences-description"
class="fixed inset-0 z-[100] overflow-y-auto p-4 sm:p-6"
class:hidden="!preferencesOpen"
class:block="preferencesOpen"
>
<div class="flex min-h-full items-end justify-center sm:items-center">
<div
class="relative w-full max-w-2xl overflow-hidden rounded-t-3xl border border-slate-200 bg-white shadow-2xl shadow-slate-950/30 dark:border-white/10 dark:bg-slate-900 dark:shadow-black/50 sm:rounded-3xl"
>
<!-- Modal header -->
<header class="border-b border-slate-200 px-5 py-5 dark:border-white/10 sm:px-6">
<div class="flex items-start justify-between gap-4">
<div class="flex items-start gap-4">
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-2xl bg-indigo-100 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300"
>
<span class="icon-[lucide--sliders-horizontal] h-5 w-5" aria-hidden="true" ></span>
</span>
<div>
<h2
id="cookie-preferences-title"
class="text-lg font-bold tracking-tight text-slate-950 dark:text-white"
>
Cookie preferences
</h2>
<p
id="cookie-preferences-description"
class="mt-1 text-sm leading-6 text-slate-500 dark:text-slate-400"
>
Choose which optional cookies WrNexus may use.
</p>
</div>
</div>
<button
type="button"
@click="preferencesOpen = false"
aria-label="Close cookie preferences"
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl border border-slate-200 bg-white text-slate-500 transition hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-400 dark:hover:bg-white/10 dark:hover:text-white"
>
<span class="icon-[lucide--x] h-4.5 w-4.5" aria-hidden="true" ></span>
</button>
</div>
</header>
<!-- Preference groups -->
<div class="max-h-[60dvh] space-y-3 overflow-y-auto p-5 sm:p-6">
<!-- Essential -->
<article
class="rounded-2xl bg-emerald-500/10 p-4 ring-1 ring-inset ring-emerald-500/15 transition dark:bg-emerald-500/10 dark:ring-emerald-400/10"
>
<div class="flex items-start justify-between gap-4">
<div class="flex items-start gap-3">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl bg-emerald-500/15 text-emerald-700 dark:bg-emerald-400/10 dark:text-emerald-300"
>
<span class="icon-[lucide--shield-check] h-4.5 w-4.5" aria-hidden="true" ></span>
</span>
<div>
<div class="flex flex-wrap items-center gap-2">
<h3 class="text-sm font-bold text-slate-950 dark:text-white">
Essential cookies
</h3>
<span
class="rounded-full bg-emerald-500/15 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-emerald-700 dark:bg-emerald-400/10 dark:text-emerald-300"
>
Always active
</span>
</div>
<p class="mt-1.5 text-xs leading-5 text-slate-600 dark:text-slate-400">
Required for authentication, security, sessions, preferences, and core website functionality.
</p>
</div>
</div>
<span
class="relative inline-flex h-6 w-11 shrink-0 cursor-not-allowed rounded-full bg-emerald-500"
aria-hidden="true"
>
<span class="absolute right-1 top-1 h-4 w-4 rounded-full bg-white shadow-sm" ></span>
</span>
</div>
</article>
<!-- Analytics -->
<article
class="rounded-2xl p-4 transition-all duration-200 {analyticsCookies ? 'bg-indigo-50 ring-1 ring-inset ring-indigo-200 dark:bg-indigo-500/10 dark:ring-indigo-500/20' : 'bg-slate-50 ring-1 ring-inset ring-slate-200 dark:bg-white/5 dark:ring-white/10' }"
>
<label class="flex cursor-pointer items-start justify-between gap-4">
<span class="flex min-w-0 items-start gap-3">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl transition-all duration-200 {analyticsCookies ? 'bg-indigo-100 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300' : 'bg-slate-200 text-slate-500 dark:bg-white/10 dark:text-slate-400' }"
>
<span class="icon-[lucide--chart-no-axes-combined] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="block text-sm font-bold text-slate-950 dark:text-white">
Analytics cookies
</span>
<span class="mt-1 block text-xs leading-5 text-slate-600 dark:text-slate-400">
Help us understand website usage, performance, and navigation patterns.
</span>
</span>
</span>
<span class="relative mt-0.5 shrink-0">
<input
type="checkbox"
name="analytics"
checked="{analyticsCookies}"
@change="analyticsCookies = event.target.checked"
class="peer sr-only"
/>
<span class="block h-6 w-11 rounded-full bg-slate-300 transition-colors duration-200 peer-checked:bg-indigo-600 peer-focus-visible:ring-4 peer-focus-visible:ring-indigo-500/20 dark:bg-slate-700" ></span>
<span class="absolute left-1 top-1 h-4 w-4 rounded-full bg-white shadow-sm transition-transform duration-200 peer-checked:translate-x-5" ></span>
</span>
</label>
</article>
<!-- Personalization -->
<article
class="rounded-2xl p-4 transition-all duration-200 {personalizationCookies ? 'bg-violet-50 ring-1 ring-inset ring-violet-200 dark:bg-violet-500/10 dark:ring-violet-500/20' : 'bg-slate-50 ring-1 ring-inset ring-slate-200 dark:bg-white/5 dark:ring-white/10' }"
>
<label class="flex cursor-pointer items-start justify-between gap-4">
<span class="flex min-w-0 items-start gap-3">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl transition-all duration-200 {personalizationCookies ? 'bg-violet-100 text-violet-700 dark:bg-violet-500/15 dark:text-violet-300' : 'bg-slate-200 text-slate-500 dark:bg-white/10 dark:text-slate-400' }"
>
<span class="icon-[lucide--sparkles] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="block text-sm font-bold text-slate-950 dark:text-white">
Personalization cookies
</span>
<span class="mt-1 block text-xs leading-5 text-slate-600 dark:text-slate-400">
Remember your choices and provide a more relevant website experience.
</span>
</span>
</span>
<span class="relative mt-0.5 shrink-0">
<input
type="checkbox"
name="personalization"
checked="{personalizationCookies}"
@change="personalizationCookies = event.target.checked"
class="peer sr-only"
/>
<span class="block h-6 w-11 rounded-full bg-slate-300 transition-colors duration-200 peer-checked:bg-violet-600 peer-focus-visible:ring-4 peer-focus-visible:ring-violet-500/20 dark:bg-slate-700" ></span>
<span class="absolute left-1 top-1 h-4 w-4 rounded-full bg-white shadow-sm transition-transform duration-200 peer-checked:translate-x-5" ></span>
</span>
</label>
</article>
<!-- Marketing -->
<article
class="rounded-2xl p-4 transition-all duration-200 {marketingCookies ? 'bg-rose-50 ring-1 ring-inset ring-rose-200 dark:bg-rose-500/10 dark:ring-rose-500/20' : 'bg-slate-50 ring-1 ring-inset ring-slate-200 dark:bg-white/5 dark:ring-white/10' }"
>
<label class="flex cursor-pointer items-start justify-between gap-4">
<span class="flex min-w-0 items-start gap-3">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl transition-all duration-200 {marketingCookies ? 'bg-rose-100 text-rose-700 dark:bg-rose-500/15 dark:text-rose-300' : 'bg-slate-200 text-slate-500 dark:bg-white/10 dark:text-slate-400' }"
>
<span class="icon-[lucide--megaphone] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="block text-sm font-bold text-slate-950 dark:text-white">
Marketing cookies
</span>
<span class="mt-1 block text-xs leading-5 text-slate-600 dark:text-slate-400">
Measure campaigns and show relevant WrNexus content across supported services.
</span>
</span>
</span>
<span class="relative mt-0.5 shrink-0">
<input
type="checkbox"
name="marketing"
checked="{marketingCookies}"
@change="marketingCookies = event.target.checked"
class="peer sr-only"
/>
<span class="block h-6 w-11 rounded-full bg-slate-300 transition-colors duration-200 peer-checked:bg-rose-600 peer-focus-visible:ring-4 peer-focus-visible:ring-rose-500/20 dark:bg-slate-700" ></span>
<span class="absolute left-1 top-1 h-4 w-4 rounded-full bg-white shadow-sm transition-transform duration-200 peer-checked:translate-x-5" ></span>
</span>
</label>
</article>
</div>
<!-- Modal footer -->
<footer
class="border-t border-slate-200 bg-slate-50 px-5 py-4 dark:border-white/10 dark:bg-white/[0.02] sm:px-6"
>
<div class="grid gap-2 sm:grid-cols-3">
<button
type="button"
@click=" analyticsCookies = false; personalizationCookies = false; marketingCookies = false "
class="inline-flex h-11 items-center justify-center rounded-xl border border-slate-200 bg-white px-4 text-sm font-semibold text-slate-700 transition hover:border-slate-300 hover:bg-slate-100 hover:text-slate-950 dark:border-white/10 dark:bg-white/5 dark:text-slate-200 dark:hover:bg-white/10 dark:hover:text-white"
>
Reject optional
</button>
<button
type="button"
@click=" analyticsCookies = true; personalizationCookies = true; marketingCookies = true "
class="inline-flex h-11 items-center justify-center rounded-xl border border-indigo-200 bg-indigo-50 px-4 text-sm font-semibold text-indigo-700 transition hover:border-indigo-300 hover:bg-indigo-100 dark:border-indigo-500/20 dark:bg-indigo-500/10 dark:text-indigo-300 dark:hover:bg-indigo-500/15"
>
Enable all
</button>
<button
type="button"
@click=" preferencesOpen = false; bannerOpen = false "
data-cookie-action="save-preferences"
class="inline-flex h-11 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-4 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl"
>
<span class="icon-[lucide--save] h-4 w-4" aria-hidden="true" ></span>
Save preferences
</button>
</div>
</footer>
</div>
</div>
</section>
</div>
}
}
@@ -0,0 +1,421 @@
component DevelopersMegaMenu {
props {
class: string = ""
}
view {
<div
class="border-t border-slate-200 bg-white shadow-2xl shadow-slate-950/10 dark:border-white/10 dark:bg-slate-950 dark:shadow-black/30 {class}"
>
<div class="mx-auto max-w-7xl px-6 py-8 lg:px-10">
<div class="grid gap-8 lg:grid-cols-[minmax(0,1.55fr)_minmax(18rem,0.8fr)]">
<!-- Developer resources -->
<section>
<div class="mb-5 flex items-end justify-between gap-6">
<div>
<p class="text-xs font-semibold uppercase tracking-[0.16em] text-indigo-600 dark:text-indigo-400">
Developers
</p>
<h2 class="mt-2 text-lg font-bold tracking-tight text-slate-950 dark:text-white">
Build secure identity into any application
</h2>
<p class="mt-1 text-sm text-slate-500 dark:text-slate-400">
Explore documentation, APIs, SDKs, examples, and tools built
for modern development teams.
</p>
</div>
<a
href="/docs"
class="group hidden items-center gap-2 text-sm font-semibold text-indigo-600 transition hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300 xl:inline-flex"
>
Open documentation
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
</div>
<div class="grid gap-2 md:grid-cols-2">
<!-- Documentation -->
<a
href="/docs"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-indigo-200 hover:bg-indigo-50 dark:hover:border-indigo-500/20 dark:hover:bg-indigo-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-indigo-100 text-indigo-700 transition group-hover:bg-indigo-600 group-hover:text-white dark:bg-indigo-500/15 dark:text-indigo-300 dark:group-hover:bg-indigo-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--book-open] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Documentation
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-indigo-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-indigo-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Guides for authentication, users, organizations, and
authorization.
</span>
</span>
</a>
<!-- API reference -->
<a
href="/docs/api"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-violet-200 hover:bg-violet-50 dark:hover:border-violet-500/20 dark:hover:bg-violet-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-violet-100 text-violet-700 transition group-hover:bg-violet-600 group-hover:text-white dark:bg-violet-500/15 dark:text-violet-300 dark:group-hover:bg-violet-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--braces] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
API reference
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-violet-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-violet-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Complete endpoint, request, response, and error
documentation.
</span>
</span>
</a>
<!-- SDKs -->
<a
href="/sdks"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-cyan-200 hover:bg-cyan-50 dark:hover:border-cyan-500/20 dark:hover:bg-cyan-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-cyan-100 text-cyan-700 transition group-hover:bg-cyan-600 group-hover:text-white dark:bg-cyan-500/15 dark:text-cyan-300 dark:group-hover:bg-cyan-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--package] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
SDKs
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-cyan-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-cyan-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Official libraries for JavaScript, TypeScript, Go, Java,
Python, and more.
</span>
</span>
</a>
<!-- Examples -->
<a
href="/examples"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-emerald-200 hover:bg-emerald-50 dark:hover:border-emerald-500/20 dark:hover:bg-emerald-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-emerald-100 text-emerald-700 transition group-hover:bg-emerald-600 group-hover:text-white dark:bg-emerald-500/15 dark:text-emerald-300 dark:group-hover:bg-emerald-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--flask-conical] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Examples
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-emerald-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-emerald-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Ready-to-run projects for common frameworks and identity
flows.
</span>
</span>
</a>
<!-- Webhooks -->
<a
href="/docs/webhooks"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-amber-200 hover:bg-amber-50 dark:hover:border-amber-500/20 dark:hover:bg-amber-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-amber-100 text-amber-700 transition group-hover:bg-amber-600 group-hover:text-white dark:bg-amber-500/15 dark:text-amber-300 dark:group-hover:bg-amber-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--webhook] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Webhooks
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-amber-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-amber-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Respond to user, session, organization, and security events.
</span>
</span>
</a>
<!-- Changelog -->
<a
href="/changelog"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-rose-200 hover:bg-rose-50 dark:hover:border-rose-500/20 dark:hover:bg-rose-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-rose-100 text-rose-700 transition group-hover:bg-rose-600 group-hover:text-white dark:bg-rose-500/15 dark:text-rose-300 dark:group-hover:bg-rose-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--history] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Changelog
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-rose-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-rose-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Review platform releases, improvements, and migration notes.
</span>
</span>
</a>
</div>
<a
href="/docs"
class="mt-4 flex items-center justify-center gap-2 rounded-xl border border-indigo-200 bg-indigo-50 px-4 py-3 text-sm font-semibold text-indigo-700 transition hover:border-indigo-300 hover:bg-indigo-100 dark:border-indigo-500/20 dark:bg-indigo-500/10 dark:text-indigo-300 dark:hover:border-indigo-500/30 dark:hover:bg-indigo-500/15 xl:hidden"
>
Open documentation
<span class="icon-[lucide--arrow-right] h-4 w-4" aria-hidden="true" ></span>
</a>
</section>
<!-- Developer toolkit -->
<aside
class="relative overflow-hidden rounded-3xl border border-indigo-200 bg-linear-to-br from-indigo-50 via-white to-violet-50 p-6 shadow-xl shadow-indigo-950/5 dark:border-indigo-500/20 dark:from-indigo-500/10 dark:via-slate-900 dark:to-violet-500/10 dark:shadow-black/20"
>
<!-- Decorative glow -->
<div aria-hidden="true" class="absolute -right-20 -top-20 h-48 w-48 rounded-full bg-indigo-300/30 blur-3xl dark:bg-indigo-500/10" ></div>
<div aria-hidden="true" class="absolute -bottom-24 -left-20 h-48 w-48 rounded-full bg-violet-300/30 blur-3xl dark:bg-violet-500/10" ></div>
<div class="relative">
<!-- Header -->
<div class="flex items-center justify-between gap-4">
<span
class="inline-flex items-center gap-2 rounded-full border border-indigo-200 bg-white/80 px-3 py-1.5 text-xs font-semibold text-indigo-700 backdrop-blur-sm dark:border-indigo-500/20 dark:bg-white/5 dark:text-indigo-300"
>
<span class="icon-[lucide--code-2] h-3.5 w-3.5" aria-hidden="true" ></span>
Developer toolkit
</span>
<span class="inline-flex items-center gap-2 text-xs font-medium text-slate-500 dark:text-slate-400">
<span class="relative flex h-2 w-2">
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-emerald-400 opacity-60"></span>
<span class="relative inline-flex h-2 w-2 rounded-full bg-emerald-500"></span>
</span>
APIs operational
</span>
</div>
<!-- Intro -->
<div
class="mt-5 grid h-12 w-12 place-items-center rounded-2xl bg-indigo-600 text-white shadow-lg shadow-indigo-600/20"
>
<span class="icon-[lucide--blocks] h-6 w-6" aria-hidden="true" ></span>
</div>
<h3 class="mt-5 text-xl font-bold tracking-tight text-slate-950 dark:text-white">
Everything developers need
</h3>
<p class="mt-3 text-sm leading-6 text-slate-600 dark:text-slate-400">
Build, test, and ship secure identity flows with APIs, SDKs,
examples, webhooks, and complete documentation.
</p>
<!-- Toolkit links -->
<div class="mt-6 grid gap-3">
<!-- Documentation -->
<a
href="/docs"
class="group flex items-center justify-between gap-4 rounded-2xl border border-slate-200 bg-white/80 px-4 py-3.5 transition hover:border-indigo-300 hover:bg-white hover:shadow-md dark:border-white/10 dark:bg-white/5 dark:hover:border-indigo-500/30 dark:hover:bg-white/10"
>
<span class="flex items-center gap-3">
<span
class="grid h-9 w-9 shrink-0 place-items-center rounded-xl bg-indigo-100 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300"
>
<span class="icon-[lucide--book-open] h-4.5 w-4.5" aria-hidden="true" ></span>
</span>
<span>
<span class="block text-sm font-semibold text-slate-950 dark:text-white">
Documentation
</span>
<span class="mt-0.5 block text-xs text-slate-500 dark:text-slate-400">
Guides and platform concepts
</span>
</span>
</span>
<span class="icon-[lucide--arrow-up-right] h-4 w-4 shrink-0 text-slate-400 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-indigo-600 dark:group-hover:text-indigo-300" aria-hidden="true" ></span>
</a>
<!-- API reference -->
<a
href="/docs/api"
class="group flex items-center justify-between gap-4 rounded-2xl border border-slate-200 bg-white/80 px-4 py-3.5 transition hover:border-violet-300 hover:bg-white hover:shadow-md dark:border-white/10 dark:bg-white/5 dark:hover:border-violet-500/30 dark:hover:bg-white/10"
>
<span class="flex items-center gap-3">
<span
class="grid h-9 w-9 shrink-0 place-items-center rounded-xl bg-violet-100 text-violet-700 dark:bg-violet-500/15 dark:text-violet-300"
>
<span class="icon-[lucide--braces] h-4.5 w-4.5" aria-hidden="true" ></span>
</span>
<span>
<span class="block text-sm font-semibold text-slate-950 dark:text-white">
API reference
</span>
<span class="mt-0.5 block text-xs text-slate-500 dark:text-slate-400">
Endpoints, requests, and responses
</span>
</span>
</span>
<span class="icon-[lucide--arrow-up-right] h-4 w-4 shrink-0 text-slate-400 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-violet-600 dark:group-hover:text-violet-300" aria-hidden="true" ></span>
</a>
<!-- SDKs -->
<a
href="/sdks"
class="group flex items-center justify-between gap-4 rounded-2xl border border-slate-200 bg-white/80 px-4 py-3.5 transition hover:border-cyan-300 hover:bg-white hover:shadow-md dark:border-white/10 dark:bg-white/5 dark:hover:border-cyan-500/30 dark:hover:bg-white/10"
>
<span class="flex items-center gap-3">
<span
class="grid h-9 w-9 shrink-0 place-items-center rounded-xl bg-cyan-100 text-cyan-700 dark:bg-cyan-500/15 dark:text-cyan-300"
>
<span class="icon-[lucide--package] h-4.5 w-4.5" aria-hidden="true" ></span>
</span>
<span>
<span class="block text-sm font-semibold text-slate-950 dark:text-white">
Official SDKs
</span>
<span class="mt-0.5 block text-xs text-slate-500 dark:text-slate-400">
Libraries for your preferred stack
</span>
</span>
</span>
<span class="icon-[lucide--arrow-up-right] h-4 w-4 shrink-0 text-slate-400 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-cyan-600 dark:group-hover:text-cyan-300" aria-hidden="true" ></span>
</a>
</div>
<!-- Actions -->
<div class="mt-6 grid grid-cols-2 gap-3">
<a
href="/docs/quickstart"
class="group flex h-11 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-4 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900"
>
Start building
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
<a
href="/examples"
class="flex h-11 items-center justify-center gap-2 rounded-xl border border-slate-200 bg-white/80 px-4 text-sm font-semibold text-slate-700 transition hover:border-indigo-300 hover:bg-white hover:text-indigo-700 dark:border-white/10 dark:bg-white/5 dark:text-slate-200 dark:hover:border-indigo-500/30 dark:hover:bg-white/10 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--flask-conical] h-4 w-4" aria-hidden="true" ></span>
Examples
</a>
</div>
</div>
</aside>
</div>
<!-- Bottom developer links -->
<div
class="mt-7 flex flex-wrap items-center justify-between gap-4 border-t border-slate-200 pt-5 dark:border-white/10"
>
<div class="flex flex-wrap items-center gap-x-6 gap-y-3">
<a
href="/docs/authentication"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--fingerprint] h-4 w-4" aria-hidden="true" ></span>
Authentication guides
</a>
<a
href="/docs/organizations"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--building-2] h-4 w-4" aria-hidden="true" ></span>
Organizations
</a>
<a
href="/docs/authorization"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--shield-keyhole] h-4 w-4" aria-hidden="true" ></span>
Authorization
</a>
<a
href="/status"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="relative flex h-2 w-2">
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-emerald-400 opacity-60"></span>
<span class="relative inline-flex h-2 w-2 rounded-full bg-emerald-500"></span>
</span>
System status
</a>
</div>
<a
href="/support/developers"
class="group inline-flex items-center gap-2 text-sm font-semibold text-indigo-600 transition hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300"
>
Developer support
<span class="icon-[lucide--arrow-up-right] h-4 w-4 transition-transform group-hover:translate-x-0.5 group-hover:-translate-y-0.5" aria-hidden="true" ></span>
</a>
</div>
</div>
</div>
}
}
+165 -11
View File
@@ -1,13 +1,167 @@
component EmptyState {
props {
class = ""
icon = ""
title = "Nothing here yet"
description = ""
actionLabel = ""
actionHref = "#"
}
view {
<div class="rounded-3xl border border-dashed border-slate-300 text-center dark:border-slate-700 {class}"><div class="mx-auto flex size-14 items-center justify-center rounded-2xl bg-violet-100 text-2xl text-violet-700 dark:bg-violet-950 dark:text-violet-300">{icon}</div><h3 class="mt-4 text-lg font-bold">{title}</h3><p data-show="description" class="mx-auto mt-2 max-w-md text-sm text-slate-500">{description}</p><a data-show="actionLabel" href="{actionHref}" class="mt-5 inline-flex min-h-11 items-center rounded-xl bg-violet-600 px-4 py-2.5 text-sm font-semibold text-white">{actionLabel}</a></div>
}
props {
class: string = ""
eyebrow = ""
title = "Nothing found"
description = ""
iconClass = "icon-[lucide--inbox]"
primaryLabel = ""
primaryHref = ""
secondaryLabel = ""
secondaryHref = ""
compact = false
centered = true
suggestions = []
}
view {
<section
aria-labelledby="empty-state-title"
class="w-full {class}"
class:py-6="compact"
class:sm:py-8="compact"
class:py-8="!compact"
class:sm:py-10="!compact"
>
<div
class=" relative mx-auto w-full overflow-hidden rounded-3xl border border-indigo-200/80 bg-linear-to-br from-indigo-50/90 via-white to-violet-50/80 shadow-xl shadow-indigo-950/5 dark:border-indigo-500/20 dark:from-indigo-500/10 dark:via-slate-900 dark:to-violet-500/10 dark:shadow-black/20 "
class:max-w-3xl="centered"
>
<!-- Background decoration -->
<div
aria-hidden="true"
class="pointer-events-none absolute inset-0 overflow-hidden"
>
<div class="absolute -right-20 -top-24 h-56 w-56 rounded-full bg-indigo-200/30 blur-3xl dark:bg-indigo-500/10"></div>
<div class="absolute -bottom-24 -left-20 h-56 w-56 rounded-full bg-violet-200/20 blur-3xl dark:bg-violet-500/5"></div>
<div class="absolute inset-x-0 top-0 h-px bg-linear-to-r from-transparent via-indigo-300/70 to-transparent dark:via-indigo-500/20"></div>
</div>
<div
class="relative"
class:px-6="compact"
class:py-8="compact"
class:sm:px-8="compact"
class:sm:py-9="compact"
class:px-6="!compact"
class:py-10="!compact"
class:sm:px-10="!compact"
class:sm:py-12="!compact"
class:lg:px-12="!compact"
class:text-center="centered"
>
<div
class=" {compact ? 'max-w-xl' : 'max-w-2xl'} {centered ? 'mx-auto' : 'mr-auto'} "
class:max-w-xl="compact"
class:max-w-2xl="!compact"
>
<!-- Icon -->
<div
class="flex"
class:justify-center="centered"
>
<span
class="grid place-items-center rounded-2xl bg-indigo-50 text-indigo-600 ring-1 ring-inset ring-indigo-200 dark:bg-indigo-500/10 dark:text-indigo-300 dark:ring-indigo-500/20"
class:h-12="compact"
class:w-12="compact"
class:h-14="!compact"
class:w-14="!compact"
>
<span class="{iconClass}" class:h-5="compact" class:w-5="compact" class:h-6="!compact" class:w-6="!compact" aria-hidden="true" ></span>
</span>
</div>
<!-- Eyebrow -->
<p
class="mt-5 text-xs font-bold uppercase tracking-[0.18em] text-indigo-600 dark:text-indigo-400"
class:hidden="eyebrow === ''"
>
{eyebrow}
</p>
<!-- Title -->
<h2
id="empty-state-title"
class="font-bold tracking-tight text-slate-950 dark:text-white"
class:mt-5="eyebrow === ''"
class:mt-3="eyebrow !== ''"
class:text-xl="compact"
class:leading-tight="compact"
class:sm:text-2xl="compact"
class:text-2xl="!compact"
class:leading-tight="!compact"
class:sm:text-3xl="!compact"
class:lg:text-[2rem]="!compact"
>
{title}
</h2>
<!-- Description -->
<p
class="mx-auto mt-3 max-w-xl text-sm leading-6 text-slate-600 dark:text-slate-400"
class:hidden="description === ''"
class:sm:text-base="!compact"
class:sm:leading-7="!compact"
>
{description}
</p>
<!-- Suggestions -->
<div
class="mt-6 flex flex-wrap gap-2"
class:hidden="suggestions.length === 0"
class:justify-center="centered"
>
{#each suggestions.slice(0, 4) as suggestion}
<a
href="{suggestion.href}"
class="inline-flex items-center gap-2 rounded-full border border-slate-200 bg-white px-3 py-2 text-xs font-semibold text-slate-600 shadow-sm transition hover:-translate-y-0.5 hover:border-indigo-300 hover:bg-indigo-50 hover:text-indigo-700 dark:border-white/10 dark:bg-white/5 dark:text-slate-300 dark:hover:border-indigo-500/30 dark:hover:bg-indigo-500/10 dark:hover:text-indigo-300"
>
<span class="{suggestion.iconClass} h-3.5 w-3.5" aria-hidden="true" ></span>
{suggestion.label}
</a>
{/each}
</div>
<!-- Actions -->
<div
class="flex flex-col gap-3 sm:flex-row"
class:mt-7="suggestions.length === 0"
class:mt-6="suggestions.length > 0"
class:hidden="primaryLabel === '' && secondaryLabel === ''"
class:justify-center="centered"
>
<a
href="{secondaryHref}"
class="h-11 items-center justify-center rounded-xl border border-slate-200 bg-white px-4 text-sm font-semibold text-slate-700 shadow-sm transition hover:-translate-y-0.5 hover:border-indigo-300 hover:bg-slate-50 hover:text-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-200 dark:hover:border-indigo-500/30 dark:hover:bg-white/10 dark:hover:text-indigo-300"
class:hidden="secondaryLabel === ''"
class:inline-flex="secondaryLabel !== ''"
>
{secondaryLabel}
</a>
<a
href="{primaryHref}"
class="group h-11 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-4 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900"
class:hidden="primaryLabel === ''"
class:inline-flex="primaryLabel !== ''"
>
{primaryLabel}
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
</div>
</div>
</div>
</div>
</section>
}
}
+191 -11
View File
@@ -1,13 +1,193 @@
component ErrorState {
props {
class = ""
code = "500"
title = "Something went wrong"
description = "Please try again."
actionLabel = "Try again"
actionHref = "/"
}
view {
<div class="max-w-2xl text-center {class}"><div class="text-8xl font-black text-violet-100 dark:text-violet-950">{code}</div><h1 class="-mt-4 text-3xl font-black">{title}</h1><p class="mt-4 text-slate-500">{description}</p><a href="{actionHref}" class="mt-8 inline-flex min-h-12 items-center rounded-xl bg-violet-600 px-5 font-semibold text-white">{actionLabel}</a></div>
}
props {
class: string = ""
type = "error"
eyebrow = ""
title = "Something went wrong"
description = ""
errorCode = ""
iconClass = ""
primaryLabel = ""
primaryHref = ""
secondaryLabel = ""
secondaryHref = ""
retryLabel = ""
retryAction = ""
centered = true
compact = false
details = []
}
view {
<section
aria-labelledby="error-state-title"
role="{type === 'error' ? 'alert' : 'status'}"
class="w-full {class}"
class:py-6="compact"
class:sm:py-8="compact"
class:py-8="!compact"
class:sm:py-10="!compact"
>
<div
class=" relative w-full overflow-hidden rounded-3xl border shadow-xl {type === 'warning' ? 'border-amber-200/80 bg-linear-to-br from-amber-50 via-white to-orange-50 shadow-amber-950/5 dark:border-amber-500/20 dark:from-amber-500/10 dark:via-slate-900 dark:to-orange-500/10' : type === 'forbidden' ? 'border-violet-200/80 bg-linear-to-br from-violet-50 via-white to-indigo-50 shadow-violet-950/5 dark:border-violet-500/20 dark:from-violet-500/10 dark:via-slate-900 dark:to-indigo-500/10' : type === 'offline' ? 'border-slate-200 bg-linear-to-br from-slate-100 via-white to-indigo-50 shadow-slate-950/5 dark:border-white/10 dark:from-white/5 dark:via-slate-900 dark:to-indigo-500/10' : 'border-rose-200/80 bg-linear-to-br from-rose-50 via-white to-orange-50 shadow-rose-950/5 dark:border-rose-500/20 dark:from-rose-500/10 dark:via-slate-900 dark:to-orange-500/10' } "
class:mx-auto="centered"
class:max-w-3xl="centered"
>
<!-- Decorative background -->
<div
aria-hidden="true"
class="pointer-events-none absolute inset-0 overflow-hidden"
>
<div class=" absolute -right-20 -top-24 h-64 w-64 rounded-full blur-3xl {type === 'warning' ? 'bg-amber-300/25 dark:bg-amber-500/10' : type === 'forbidden' ? 'bg-violet-300/25 dark:bg-violet-500/10' : type === 'offline' ? 'bg-slate-300/25 dark:bg-slate-500/10' : 'bg-rose-300/25 dark:bg-rose-500/10' } " ></div>
<div class=" absolute -bottom-28 -left-20 h-64 w-64 rounded-full blur-3xl {type === 'warning' ? 'bg-orange-300/20 dark:bg-orange-500/10' : type === 'forbidden' ? 'bg-indigo-300/20 dark:bg-indigo-500/10' : type === 'offline' ? 'bg-indigo-300/15 dark:bg-indigo-500/5' : 'bg-orange-300/20 dark:bg-orange-500/10' } " ></div>
<div class=" absolute left-1/2 top-0 h-px w-2/3 -translate-x-1/2 bg-linear-to-r from-transparent to-transparent {type === 'warning' ? 'via-amber-400/60 dark:via-amber-400/20' : type === 'forbidden' ? 'via-violet-400/60 dark:via-violet-400/20' : type === 'offline' ? 'via-slate-400/50 dark:via-slate-400/20' : 'via-rose-400/60 dark:via-rose-400/20' } " ></div>
</div>
<div
class="relative"
class:px-6="compact"
class:py-8="compact"
class:sm:px-8="compact"
class:sm:py-9="compact"
class:px-6="!compact"
class:py-10="!compact"
class:sm:px-10="!compact"
class:sm:py-12="!compact"
class:lg:px-12="!compact"
class:text-center="centered"
class:text-left="!centered"
>
<div class="{compact ? 'max-w-xl' : 'max-w-2xl'} {centered ? 'mx-auto' : 'mr-auto'}" >
<!-- Icon -->
<div
class="flex"
class:justify-center="centered"
class:justify-start="!centered"
>
<div class="relative">
<div aria-hidden="true" class=" absolute inset-1 rounded-2xl blur-xl {type === 'warning' ? 'bg-amber-500/25' : type === 'forbidden' ? 'bg-violet-500/25' : type === 'offline' ? 'bg-slate-500/20' : 'bg-rose-500/25' } " ></div>
<span
class=" relative grid place-items-center rounded-2xl border bg-white/90 shadow-lg backdrop-blur-sm {compact ? 'h-12 w-12' : 'h-14 w-14'} {type === 'warning' ? 'border-amber-200 text-amber-700 shadow-amber-950/10 dark:border-amber-500/20 dark:bg-amber-500/10 dark:text-amber-300' : type === 'forbidden' ? 'border-violet-200 text-violet-700 shadow-violet-950/10 dark:border-violet-500/20 dark:bg-violet-500/10 dark:text-violet-300' : type === 'offline' ? 'border-slate-200 text-slate-700 shadow-slate-950/10 dark:border-white/10 dark:bg-white/5 dark:text-slate-300' : 'border-rose-200 text-rose-700 shadow-rose-950/10 dark:border-rose-500/20 dark:bg-rose-500/10 dark:text-rose-300' } "
>
<span class=" {compact ? 'h-5 w-5' : 'h-6 w-6'} {iconClass !== '' ? iconClass : type === 'warning' ? 'icon-[lucide--triangle-alert]' : type === 'forbidden' ? 'icon-[lucide--shield-alert]' : type === 'offline' ? 'icon-[lucide--wifi-off]' : 'icon-[lucide--circle-x]' } " aria-hidden="true" ></span>
</span>
</div>
</div>
<!-- Error code -->
<div
class="mt-5 flex"
class:hidden="errorCode === ''"
class:justify-center="centered"
class:justify-start="!centered"
>
<span
class=" rounded-full px-3 py-1 text-xs font-bold tracking-[0.14em] {type === 'warning' ? 'bg-amber-100 text-amber-700 dark:bg-amber-500/15 dark:text-amber-300' : type === 'forbidden' ? 'bg-violet-100 text-violet-700 dark:bg-violet-500/15 dark:text-violet-300' : type === 'offline' ? 'bg-slate-200 text-slate-700 dark:bg-white/10 dark:text-slate-300' : 'bg-rose-100 text-rose-700 dark:bg-rose-500/15 dark:text-rose-300' } "
>
{errorCode}
</span>
</div>
<!-- Eyebrow -->
<p
class=" text-xs font-bold uppercase tracking-[0.18em] {errorCode !== '' ? 'mt-4' : 'mt-5'} {type === 'warning' ? 'text-amber-700 dark:text-amber-400' : type === 'forbidden' ? 'text-violet-700 dark:text-violet-400' : type === 'offline' ? 'text-slate-600 dark:text-slate-400' : 'text-rose-700 dark:text-rose-400' } "
class:hidden="eyebrow === ''"
>
{eyebrow}
</p>
<!-- Title -->
<h2
id="error-state-title"
class=" font-bold leading-tight tracking-tight text-slate-950 dark:text-white {eyebrow !== '' || errorCode !== '' ? 'mt-3' : 'mt-5'} {compact ? 'text-xl sm:text-2xl' : 'text-2xl sm:text-3xl lg:text-[2rem]'} "
>
{title}
</h2>
<!-- Description -->
<p
class=" mt-3 max-w-xl text-sm leading-6 text-slate-600 dark:text-slate-400 {!compact ? 'sm:text-base sm:leading-7' : ''} {centered ? 'mx-auto' : 'mr-auto'} "
class:hidden="description === ''"
>
{description}
</p>
<!-- Details -->
<div
class="mt-6 flex flex-wrap gap-x-5 gap-y-3"
class:hidden="details.length === 0"
class:justify-center="centered"
class:justify-start="!centered"
>
{#each details.slice(0, 4) as detail}
<div class="inline-flex items-center gap-2 text-sm font-medium text-slate-600 dark:text-slate-300">
<span
class=" grid h-6 w-6 shrink-0 place-items-center rounded-full {type === 'warning' ? 'bg-amber-100 text-amber-700 dark:bg-amber-500/15 dark:text-amber-300' : type === 'forbidden' ? 'bg-violet-100 text-violet-700 dark:bg-violet-500/15 dark:text-violet-300' : type === 'offline' ? 'bg-slate-200 text-slate-700 dark:bg-white/10 dark:text-slate-300' : 'bg-rose-100 text-rose-700 dark:bg-rose-500/15 dark:text-rose-300' } "
>
<span class="{detail.iconClass} h-3.5 w-3.5" aria-hidden="true" ></span>
</span>
{detail.label}
</div>
{/each}
</div>
<!-- Actions -->
<div
class="mt-7 flex flex-col gap-3 sm:flex-row"
class:hidden="primaryLabel === '' && secondaryLabel === '' && retryLabel === ''"
class:justify-center="centered"
class:justify-start="!centered"
>
<!-- Secondary -->
<a
href="{secondaryHref}"
class="h-11 items-center justify-center rounded-xl border border-slate-200 bg-white/90 px-4 text-sm font-semibold text-slate-700 shadow-sm backdrop-blur-sm transition hover:-translate-y-0.5 hover:border-indigo-300 hover:bg-white hover:text-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-200 dark:hover:border-indigo-500/30 dark:hover:bg-white/10 dark:hover:text-indigo-300"
class:hidden="secondaryLabel === ''"
class:inline-flex="secondaryLabel !== ''"
>
{secondaryLabel}
</a>
<!-- Retry -->
<button
type="button"
@click="{retryAction}"
class=" group h-11 items-center justify-center gap-2 rounded-xl px-4 text-sm font-bold text-white shadow-lg transition hover:-translate-y-0.5 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 {type === 'warning' ? 'bg-amber-600 shadow-amber-600/20 hover:bg-amber-500 focus-visible:ring-amber-500' : type === 'forbidden' ? 'bg-violet-600 shadow-violet-600/20 hover:bg-violet-500 focus-visible:ring-violet-500' : type === 'offline' ? 'bg-slate-800 shadow-slate-900/20 hover:bg-slate-700 focus-visible:ring-slate-500 dark:bg-slate-200 dark:text-slate-950 dark:hover:bg-white' : 'bg-rose-600 shadow-rose-600/20 hover:bg-rose-500 focus-visible:ring-rose-500' } "
class:hidden="retryLabel === ''"
class:inline-flex="retryLabel !== ''"
>
<span class="icon-[lucide--rotate-cw] h-4 w-4 transition-transform group-hover:rotate-45" aria-hidden="true" ></span>
{retryLabel}
</button>
<!-- Primary link -->
<a
href="{primaryHref}"
class=" group h-11 items-center justify-center gap-2 rounded-xl px-4 text-sm font-bold text-white shadow-lg transition hover:-translate-y-0.5 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 {type === 'warning' ? 'bg-amber-600 shadow-amber-600/20 hover:bg-amber-500 focus-visible:ring-amber-500' : type === 'forbidden' ? 'bg-violet-600 shadow-violet-600/20 hover:bg-violet-500 focus-visible:ring-violet-500' : type === 'offline' ? 'bg-slate-800 shadow-slate-900/20 hover:bg-slate-700 focus-visible:ring-slate-500 dark:bg-slate-200 dark:text-slate-950 dark:hover:bg-white' : 'bg-rose-600 shadow-rose-600/20 hover:bg-rose-500 focus-visible:ring-rose-500' } "
class:hidden="primaryLabel === ''"
class:inline-flex="primaryLabel !== ''"
>
{primaryLabel}
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
</div>
</div>
</div>
</div>
</section>
}
}
+148 -7
View File
@@ -1,9 +1,150 @@
component LogoCloud {
props {
class = ""
title = "Trusted by modern teams"
}
view {
<section class="{class}" aria-label="{title}"><p class="text-center text-sm font-semibold uppercase tracking-wider text-slate-500">{title}</p><div class="mt-6 flex flex-wrap items-center justify-center gap-x-10 gap-y-6 opacity-70 grayscale transition hover:grayscale-0"><slot /></div></section>
}
props {
class: string = ""
eyebrow = ""
title = "Trusted by teams building modern products"
description = ""
logos = []
centered = true
compact = false
variant = "strip"
grayscale = true
showNames = false
maxItems = 8
}
view {
<section
aria-labelledby="logo-cloud-title"
class="w-full px-5 sm:px-8 lg:px-10 xl:px-12 {class}"
class:py-10="compact"
class:sm:py-12="compact"
class:py-14="!compact"
class:sm:py-16="!compact"
class:lg:py-20="!compact"
class:hidden="logos.length === 0"
>
<div
class=" relative mx-auto w-full max-w-7xl overflow-hidden {variant === 'card' ? 'rounded-3xl border border-indigo-200/80 bg-linear-to-br from-indigo-50/80 via-white to-violet-50/70 shadow-xl shadow-indigo-950/5 dark:border-indigo-500/20 dark:from-indigo-500/10 dark:via-slate-900 dark:to-violet-500/10 dark:shadow-black/20' : '' } "
>
<!-- Decorative background -->
<div
aria-hidden="true"
class="pointer-events-none absolute inset-0 overflow-hidden"
class:hidden="variant !== 'card'"
>
<div class="absolute -right-24 -top-28 h-64 w-64 rounded-full bg-indigo-300/20 blur-3xl dark:bg-indigo-500/10"></div>
<div class="absolute -bottom-28 -left-24 h-64 w-64 rounded-full bg-violet-300/15 blur-3xl dark:bg-violet-500/5"></div>
<div class="absolute left-1/2 top-0 h-px w-2/3 -translate-x-1/2 bg-linear-to-r from-transparent via-indigo-400/60 to-transparent dark:via-indigo-400/20"></div>
</div>
<div
class="relative"
class:px-6="compact"
class:py-8="compact"
class:sm:px-8="compact"
class:sm:py-10="compact"
class:px-7="!compact"
class:py-10="!compact"
class:sm:px-10="!compact"
class:sm:py-12="!compact"
class:lg:px-12="!compact"
class:lg:py-14="!compact"
>
<!-- Heading -->
<div
class="max-w-3xl"
class:mx-auto="centered"
class:text-center="centered"
>
<p
class="text-xs font-bold uppercase tracking-[0.18em] text-indigo-600 dark:text-indigo-400"
class:hidden="eyebrow === ''"
>
{eyebrow}
</p>
<h2
id="logo-cloud-title"
class=" font-bold leading-tight tracking-tight text-slate-950 dark:text-white {eyebrow !== '' ? 'mt-3' : ''} {compact ? 'text-2xl sm:text-3xl' : 'text-3xl sm:text-4xl' } "
>
{title}
</h2>
<p
class=" mt-4 text-sm leading-7 text-slate-600 dark:text-slate-400 {centered ? 'mx-auto max-w-2xl' : 'max-w-2xl'} {!compact ? 'sm:text-base' : ''} "
class:hidden="description === ''"
>
{description}
</p>
</div>
<!-- Dynamic logo list -->
<div
class=" mt-8 flex flex-wrap justify-center {variant === 'cards' ? 'gap-4 sm:mt-10' : 'items-center gap-x-10 gap-y-7 sm:mt-10 lg:gap-x-14' } "
>
{#each logos.slice(0, maxItems) as logo}
<a
href="{logo.href}"
target="{logo.external ? '_blank' : '_self'}"
rel="{logo.external ? 'noopener noreferrer' : ''}"
aria-label="{logo.name}"
class=" group min-w-0 transition duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 {variant === 'cards' ? 'flex w-full items-center gap-4 rounded-2xl border border-slate-200/80 bg-white/80 p-4 shadow-sm backdrop-blur-sm hover:-translate-y-1 hover:border-indigo-300 hover:bg-white hover:shadow-lg dark:border-white/10 dark:bg-white/5 dark:hover:border-indigo-500/30 dark:hover:bg-white/10 sm:w-[calc(50%-0.5rem)] lg:w-[calc(25%-0.75rem)]' : 'flex h-12 items-center justify-center px-2 opacity-70 hover:opacity-100' } "
>
<!-- Logo mark -->
<div
class=" flex shrink-0 items-center justify-center {variant === 'cards' ? 'h-12 w-12 rounded-xl bg-slate-50 ring-1 ring-inset ring-slate-200 dark:bg-white/5 dark:ring-white/10' : '' } "
>
<img
src="{logo.src}"
alt="{logo.alt !== '' ? logo.alt : logo.name}"
loading="lazy"
class=" block object-contain transition duration-200 {variant === 'cards' ? 'max-h-7 max-w-9' : 'max-h-8 max-w-36' } {grayscale ? 'grayscale group-hover:grayscale-0' : '' } "
/>
<!-- Fallback mark -->
<span
class=" hidden h-8 w-8 items-center justify-center rounded-lg bg-indigo-50 text-sm font-bold text-indigo-700 dark:bg-indigo-500/10 dark:text-indigo-300 "
>
{logo.name.slice(0, 1)}
</span>
</div>
<!-- Partner text -->
<div
class="min-w-0"
class:hidden="variant !== 'cards' && !showNames"
>
<p class="truncate text-sm font-semibold text-slate-800 dark:text-slate-200">
{logo.name}
</p>
<p
class="mt-0.5 truncate text-xs text-slate-500 dark:text-slate-400"
class:hidden="logo.description === ''"
>
{logo.description}
</p>
</div>
</a>
{/each}
</div>
<!-- Optional count -->
<p
class="mt-6 text-sm text-slate-500 dark:text-slate-400"
class:hidden="logos.length <= maxItems"
class:text-center="centered"
>
And {logos.length - maxItems} more teams using WrNexus.
</p>
</div>
</div>
</section>
}
}
+212 -11
View File
@@ -1,13 +1,214 @@
component NewsletterForm {
props {
eyebrow = ""
title = ""
description = ""
href = ""
actionLabel = "Learn more"
image = ""
alt = ""
class = ""
}
view { <article class="wire-card wire-catalog-card {class}"><img data-show="image" src="{image}" alt="{alt}" class="wire-catalog-card__image" /><span data-show="eyebrow" class="wire-eyebrow">{eyebrow}</span><h3 data-show="title">{title}</h3><p data-show="description">{description}</p><slot /><a data-show="href" href="{href}" class="wire-btn wire-btn--ghost">{actionLabel}</a></article> }
props {
class: string = ""
eyebrow = ""
title = "Stay up to date"
description = ""
placeholder = "Enter your email"
buttonLabel = "Subscribe"
action = "/newsletter/subscribe"
method = "post"
emailName = "email"
privacyLabel = ""
privacyHref = "/privacy"
successMessage = "You are subscribed."
errorMessage = "Something went wrong. Please try again."
iconClass = "icon-[lucide--mail]"
centered = false
compact = false
showIcon = true
showPrivacy = true
fullWidth = false
}
state email = ""
state status = "idle"
state message = ""
view {
<section
aria-labelledby="newsletter-form-title"
class="w-full px-5 sm:px-8 lg:px-10 xl:px-12 {class}"
>
<div
class=" relative mx-auto w-full overflow-hidden rounded-3xl border border-indigo-200/80 bg-linear-to-br from-indigo-50 via-white to-violet-50 shadow-xl shadow-indigo-950/5 dark:border-indigo-500/20 dark:from-indigo-500/10 dark:via-slate-900 dark:to-violet-500/10 dark:shadow-black/20 "
class:max-w-7xl="fullWidth"
class:max-w-3xl="!fullWidth"
>
<!-- Background decoration -->
<div
aria-hidden="true"
class="pointer-events-none absolute inset-0 overflow-hidden"
>
<div class="absolute -right-20 -top-24 h-64 w-64 rounded-full bg-indigo-300/25 blur-3xl dark:bg-indigo-500/10"></div>
<div class="absolute -bottom-28 -left-20 h-64 w-64 rounded-full bg-violet-300/20 blur-3xl dark:bg-violet-500/10"></div>
<div class="absolute left-1/2 top-0 h-px w-2/3 -translate-x-1/2 bg-linear-to-r from-transparent via-indigo-400/60 to-transparent dark:via-indigo-400/20"></div>
</div>
<div
class="relative"
class:p-5="compact"
class:sm:p-6="compact"
class:p-7="!compact"
class:sm:p-9="!compact"
class:lg:p-10="!compact"
>
<div
class=" {compact ? 'flex flex-col gap-5 lg:flex-row lg:items-center lg:justify-between lg:gap-10' : centered ? 'mx-auto flex max-w-2xl flex-col items-center gap-7 text-center' : 'grid gap-8 lg:grid-cols-[minmax(0,1fr)_minmax(360px,480px)] lg:items-center lg:gap-12' } "
>
<!-- Content -->
<div
class=" min-w-0 {compact ? 'max-w-2xl' : ''} {centered && !compact ? 'w-full max-w-2xl' : ''} "
>
<div class=" flex gap-4 {centered && !compact ? 'flex-col items-center' : 'items-start' } " >
<!-- Icon -->
<div
class="relative shrink-0"
class:hidden="!showIcon"
>
<div aria-hidden="true" class="absolute inset-1 rounded-2xl bg-indigo-500/25 blur-xl" ></div>
<span
class=" relative grid place-items-center rounded-2xl border border-indigo-200 bg-white/90 text-indigo-600 shadow-lg shadow-indigo-950/10 backdrop-blur-sm dark:border-indigo-500/20 dark:bg-indigo-500/10 dark:text-indigo-300 dark:shadow-black/20 {compact ? 'h-11 w-11' : 'h-13 w-13'} "
>
<span class=" {iconClass} {compact ? 'h-5 w-5' : 'h-6 w-6'} " aria-hidden="true" ></span>
</span>
</div>
<div class="min-w-0">
<!-- Eyebrow -->
<p
class="text-xs font-bold uppercase tracking-[0.18em] text-indigo-600 dark:text-indigo-400"
class:hidden="eyebrow === ''"
>
{eyebrow}
</p>
<!-- Title -->
<h2
id="newsletter-form-title"
class=" font-bold leading-tight tracking-tight text-slate-950 dark:text-white {eyebrow !== '' ? 'mt-2' : ''} {compact ? 'text-xl sm:text-2xl' : 'text-2xl sm:text-3xl' } "
>
{title}
</h2>
</div>
</div>
<!-- Description -->
<p
class=" mt-4 text-sm leading-6 text-slate-600 dark:text-slate-400 {!compact ? 'sm:text-base sm:leading-7' : ''} {centered && !compact ? 'mx-auto max-w-xl' : 'max-w-2xl' } "
class:hidden="description === ''"
>
{description}
</p>
</div>
<!-- Form -->
<div class=" w-full {compact ? 'lg:max-w-xl' : ''} {centered && !compact ? 'max-w-2xl' : ''} " >
<form
action="{action}"
method="{method}"
class="w-full"
@submit=" status = 'loading'; message = ''; "
>
<label
for="newsletter-email"
class="sr-only"
>
Email address
</label>
<div
class=" flex w-full flex-col gap-2 rounded-2xl border border-slate-200 bg-white/85 p-2 shadow-sm backdrop-blur-sm dark:border-white/10 dark:bg-white/5 sm:flex-row "
>
<div class="relative min-w-0 flex-1">
<span class="pointer-events-none absolute left-3.5 top-1/2 h-4.5 w-4.5 -translate-y-1/2 text-slate-400 icon-[lucide--mail] dark:text-slate-500" aria-hidden="true" ></span>
<input
id="newsletter-email"
type="email"
name="{emailName}"
value="{email}"
placeholder="{placeholder}"
autocomplete="email"
required
@input="email = event.target.value"
class=" w-full rounded-xl bg-transparent pl-10 pr-3 text-sm text-slate-950 outline-none placeholder:text-slate-400 focus:bg-white dark:text-white dark:placeholder:text-slate-500 dark:focus:bg-white/5 {compact ? 'h-10' : 'h-11'} "
/>
</div>
<button
type="submit"
class=" group inline-flex shrink-0 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-5 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 disabled:cursor-wait disabled:opacity-70 dark:focus-visible:ring-offset-slate-900 {compact ? 'h-10' : 'h-11'} "
disabled="{status === 'loading'}"
>
<span class="icon-[lucide--loader-circle] h-4 w-4 animate-spin" aria-hidden="true" class:hidden="status !== 'loading'" ></span>
<span class:hidden="status === 'loading'">
{buttonLabel}
</span>
<span class:hidden="status !== 'loading'">
Subscribing
</span>
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" class:hidden="status === 'loading'" ></span>
</button>
</div>
</form>
<p
class="mt-3 text-xs leading-5 text-slate-500 dark:text-slate-400"
class:hidden="!showPrivacy"
class:text-center="centered && !compact"
>
No spam. Unsubscribe at any time.
<a
href="{privacyHref}"
class="font-semibold text-indigo-600 transition hover:text-indigo-500 dark:text-indigo-400 dark:hover:text-indigo-300"
class:hidden="privacyLabel === ''"
>
{privacyLabel}
</a>
</p>
<div
role="status"
aria-live="polite"
class="mt-3 rounded-xl px-3 py-2.5 text-sm font-medium"
class:hidden="message === ''"
class:bg-emerald-50="status === 'success'"
class:text-emerald-700="status === 'success'"
class:ring-1="status === 'success'"
class:ring-inset="status === 'success'"
class:ring-emerald-200="status === 'success'"
class:dark:bg-emerald-500/10="status === 'success'"
class:dark:text-emerald-300="status === 'success'"
class:dark:ring-emerald-500/20="status === 'success'"
class:bg-rose-50="status === 'error'"
class:text-rose-700="status === 'error'"
class:ring-1="status === 'error'"
class:ring-inset="status === 'error'"
class:ring-rose-200="status === 'error'"
class:dark:bg-rose-500/10="status === 'error'"
class:dark:text-rose-300="status === 'error'"
class:dark:ring-rose-500/20="status === 'error'"
>
{message}
</div>
</div>
</div>
</div>
</div>
</section>
}
}
+172
View File
@@ -0,0 +1,172 @@
component PageHeader {
props {
class: string = ""
eyebrow = ""
title = ""
description = ""
primaryLabel = ""
primaryHref = ""
secondaryLabel = ""
secondaryHref = ""
icon = "sparkles"
centered = false
compact = false
showBreadcrumbs = false
breadcrumbParent = ""
breadcrumbParentHref = ""
breadcrumbCurrent = ""
highlights = []
}
view {
<section
aria-labelledby="public-page-title"
class="relative overflow-hidden border-b border-slate-200 bg-white dark:border-white/10 dark:bg-slate-950 {class}"
>
<!-- Background decoration -->
<div
aria-hidden="true"
class="pointer-events-none absolute inset-0 overflow-hidden"
>
<div class="absolute -right-40 -top-48 h-96 w-96 rounded-full bg-indigo-300/20 blur-3xl dark:bg-indigo-500/10" ></div>
<div class="absolute -left-40 top-20 h-80 w-80 rounded-full bg-violet-300/15 blur-3xl dark:bg-violet-500/5" ></div>
<div class="absolute left-1/2 top-0 h-px w-2/3 -translate-x-1/2 bg-linear-to-r from-transparent via-indigo-300/60 to-transparent dark:via-indigo-500/20" ></div>
</div>
<div class="relative w-full px-5 sm:px-8 lg:px-10 xl:px-12">
<!-- Breadcrumbs -->
<div
class="border-b border-slate-200/80 py-3 dark:border-white/10"
class:hidden="!showBreadcrumbs"
>
<Breadcrumbs
centered="{centered}"
compact="{false}"
items="{[ ...(breadcrumbParent !== '' ? [{ label: breadcrumbParent, href: breadcrumbParentHref, current: false }] : []), ...(breadcrumbCurrent !== '' ? [{ label: breadcrumbCurrent, href: '', current: true }] : []) ]}"
/>
</div>
<!-- Header body -->
<div
class=" {compact ? 'py-9 sm:py-10 lg:py-12' : 'py-12 sm:py-14 lg:py-16' } {centered ? 'mx-auto flex max-w-5xl flex-col items-center text-center' : 'grid gap-8 lg:grid-cols-[minmax(0,1fr)_auto] lg:items-start lg:gap-12' } "
>
<!-- Content -->
<div class=" min-w-0 {centered ? 'w-full max-w-4xl' : 'max-w-4xl'} " >
<!-- Eyebrow -->
<div
class="flex"
class:hidden="eyebrow === ''"
class:justify-center="centered"
class:justify-start="!centered"
>
<span
class="inline-flex items-center gap-2 rounded-full border border-indigo-200 bg-indigo-50 px-3 py-1.5 text-xs font-semibold text-indigo-700 shadow-sm dark:border-indigo-500/20 dark:bg-indigo-500/10 dark:text-indigo-300"
>
<span class="grid h-5 w-5 place-items-center rounded-full bg-indigo-100 dark:bg-indigo-500/15" >
<span class="icon-[lucide--{icon}] h-3 w-3" aria-hidden="true" ></span>
</span>
{eyebrow}
</span>
</div>
<!-- Title -->
<h1
id="public-page-title"
class=" font-bold tracking-tight text-slate-950 dark:text-white {eyebrow !== '' ? 'mt-6' : ''} {compact ? 'text-3xl leading-tight sm:text-4xl lg:text-5xl' : 'text-4xl leading-[1.08] sm:text-5xl lg:text-[3.25rem]' } "
>
{title}
</h1>
<!-- Description -->
<p
class=" mt-5 text-base text-slate-600 dark:text-slate-400 {compact ? 'leading-7' : 'leading-8 sm:text-lg'} {centered ? 'mx-auto max-w-3xl' : 'max-w-2xl'} "
class:hidden="description === ''"
>
{description}
</p>
</div>
<!-- Single dynamic action group -->
<div
class=" flex flex-col gap-3 sm:flex-row {centered ? 'mt-8 justify-center' : 'lg:pt-1' } "
class:hidden="primaryLabel === '' && secondaryLabel === ''"
>
<!-- Secondary action -->
<a
href="{secondaryHref}"
class="h-12 items-center justify-center rounded-xl border border-slate-200 bg-white px-5 text-sm font-semibold text-slate-700 shadow-sm transition hover:-translate-y-0.5 hover:border-indigo-300 hover:bg-slate-50 hover:text-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-200 dark:hover:border-indigo-500/30 dark:hover:bg-white/10 dark:hover:text-indigo-300"
class:hidden="secondaryLabel === ''"
class:inline-flex="secondaryLabel !== ''"
>
{secondaryLabel}
</a>
<!-- Primary action -->
<a
href="{primaryHref}"
class="group h-12 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-5 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-950"
class:hidden="primaryLabel === ''"
class:inline-flex="primaryLabel !== ''"
>
{primaryLabel}
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
</div>
</div>
<!-- Dynamic highlight strip -->
<div
class="border-t border-slate-200 py-5 dark:border-white/10"
class:hidden="compact || highlights.length === 0"
class:block="!compact && highlights.length > 0"
>
<div class="flex gap-y-5">
{#each highlights as highlight}
<div
class="w-full min-w-0 pr-6"
class:w-full="highlights.length === 1"
class:sm:w-1/2="highlights.length >= 2"
class:lg:w-1/2="highlights.length === 2"
class:lg:w-1/3="highlights.length === 3"
class:lg:w-1/4="highlights.length >= 4"
>
<div
class="flex min-w-0 items-center gap-3"
class:justify-center="centered"
>
<span class="grid h-10 w-10 shrink-0 place-items-center rounded-xl {highlight.iconBoxClass}" >
<span class="{highlight.iconClass} h-5 w-5" aria-hidden="true" ></span>
</span>
<div class="min-w-0">
<p class="text-sm font-semibold text-slate-950 dark:text-white">
{highlight.title}
</p>
<p
class="mt-0.5 text-xs leading-5 text-slate-500 dark:text-slate-400"
class:hidden="highlight.description === ''"
>
{highlight.description}
</p>
</div>
</div>
</div>
{/each}
</div>
</div>
</div>
</section>
}
}
+185 -10
View File
@@ -1,12 +1,187 @@
component Pagination {
props {
class = ""
page = 1
totalPages = 1
previousHref = "#"
nextHref = "#"
}
view {
<nav aria-label="Pagination" class="flex items-center justify-between gap-4 {class}"><a href="{previousHref}" aria-disabled="{page <= 1}" class="inline-flex min-h-11 items-center rounded-xl border border-slate-300 px-4 text-sm font-semibold aria-disabled:pointer-events-none aria-disabled:opacity-50 dark:border-slate-700">← Previous</a><span class="text-sm text-slate-500">Page {page} of {totalPages}</span><a href="{nextHref}" aria-disabled="{page >= totalPages}" class="inline-flex min-h-11 items-center rounded-xl border border-slate-300 px-4 text-sm font-semibold aria-disabled:pointer-events-none aria-disabled:opacity-50 dark:border-slate-700">Next →</a></nav>
}
props {
class: string = ""
currentPage = 1
totalPages = 1
previousHref = ""
nextHref = ""
pages = []
showNumbers = true
showSummary = true
totalItems = 0
pageSize = 10
compact = false
centered = false
}
view {
<nav
aria-label="Pagination"
class="w-full {class}"
class:hidden="totalPages <= 1"
>
<!-- Result summary -->
<div
class="mb-4 text-sm text-slate-500 dark:text-slate-400"
class:hidden="!showSummary || totalItems <= 0"
class:text-center="centered"
>
Showing
<span class="font-semibold text-slate-950 dark:text-white">
{(currentPage - 1) * pageSize + 1}
</span>
to
<span class="font-semibold text-slate-950 dark:text-white">
{currentPage * pageSize > totalItems ? totalItems : currentPage * pageSize}
</span>
of
<span class="font-semibold text-slate-950 dark:text-white">
{totalItems}
</span>
results
</div>
<div
class="flex flex-col gap-4 sm:flex-row sm:items-center"
class:sm:justify-center="centered"
class:sm:justify-between="!centered"
>
<!-- Previous -->
<a
href="{previousHref}"
aria-label="Go to previous page"
class="group h-11 items-center justify-center gap-2 rounded-xl border border-slate-200 bg-white px-4 text-sm font-semibold text-slate-700 shadow-sm transition hover:-translate-y-0.5 hover:border-indigo-300 hover:bg-slate-50 hover:text-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-200 dark:hover:border-indigo-500/30 dark:hover:bg-white/10 dark:hover:text-indigo-300"
class:hidden="previousHref === ''"
class:inline-flex="previousHref !== ''"
>
<span class="icon-[lucide--arrow-left] h-4 w-4 transition-transform group-hover:-translate-x-0.5" aria-hidden="true" ></span>
<span class:hidden="compact">
Previous
</span>
</a>
<span
aria-disabled="true"
class="h-11 cursor-not-allowed items-center justify-center gap-2 rounded-xl border border-slate-200 bg-slate-50 px-4 text-sm font-semibold text-slate-400 dark:border-white/10 dark:bg-white/[0.03] dark:text-slate-600"
class:hidden="previousHref !== ''"
class:inline-flex="previousHref === ''"
>
<span class="icon-[lucide--arrow-left] h-4 w-4" aria-hidden="true" ></span>
<span class:hidden="compact">
Previous
</span>
</span>
<!-- Numbered pages -->
<div
class="flex flex-wrap items-center justify-center gap-1.5"
class:hidden="!showNumbers"
>
{#each pages as page}
<!-- Ellipsis -->
<span
class="grid h-10 w-10 place-items-center text-sm font-semibold text-slate-400"
class:hidden="!page.ellipsis"
>
</span>
<!-- Page link -->
<a
href="{page.href}"
aria-label="Go to page {page.number}"
class="h-10 min-w-10 items-center justify-center rounded-xl px-3 text-sm font-semibold transition focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500"
class:hidden="page.ellipsis || page.current"
class:inline-flex="!page.ellipsis && !page.current"
class:border="!page.current"
class:border-slate-200="!page.current"
class:bg-white="!page.current"
class:text-slate-600="!page.current"
class:hover:border-indigo-300="!page.current"
class:hover:bg-indigo-50="!page.current"
class:hover:text-indigo-700="!page.current"
class:dark:border-white/10="!page.current"
class:dark:bg-white/5="!page.current"
class:dark:text-slate-300="!page.current"
class:dark:hover:border-indigo-500/30="!page.current"
class:dark:hover:bg-indigo-500/10="!page.current"
class:dark:hover:text-indigo-300="!page.current"
>
{page.number}
</a>
<!-- Current page -->
<span
aria-current="page"
aria-label="Current page, page {page.number}"
class="h-10 min-w-10 items-center justify-center rounded-xl bg-indigo-600 px-3 text-sm font-bold text-white shadow-lg shadow-indigo-600/20"
class:hidden="page.ellipsis || !page.current"
class:inline-flex="!page.ellipsis && page.current"
>
{page.number}
</span>
{/each}
</div>
<!-- Mobile page counter -->
<div
class="flex items-center justify-center rounded-xl border border-slate-200 bg-slate-50 px-4 py-2.5 text-sm font-medium text-slate-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-400"
class:hidden="showNumbers"
>
Page
<span class="mx-1 font-bold text-slate-950 dark:text-white">
{currentPage}
</span>
of
<span class="ml-1 font-bold text-slate-950 dark:text-white">
{totalPages}
</span>
</div>
<!-- Next -->
<a
href="{nextHref}"
aria-label="Go to next page"
class="group h-11 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-4 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-950"
class:hidden="nextHref === ''"
class:inline-flex="nextHref !== ''"
>
<span class:hidden="compact">
Next
</span>
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-0.5" aria-hidden="true" ></span>
</a>
<span
aria-disabled="true"
class="h-11 cursor-not-allowed items-center justify-center gap-2 rounded-xl border border-slate-200 bg-slate-50 px-4 text-sm font-semibold text-slate-400 dark:border-white/10 dark:bg-white/[0.03] dark:text-slate-600"
class:hidden="nextHref !== ''"
class:inline-flex="nextHref === ''"
>
<span class:hidden="compact">
Next
</span>
<span class="icon-[lucide--arrow-right] h-4 w-4" aria-hidden="true" ></span>
</span>
</div>
</nav>
}
}
+332
View File
@@ -0,0 +1,332 @@
component ProductMegaMenu {
props {
class: string = ""
}
view {
<div
class="border-t border-slate-200 bg-white shadow-2xl shadow-slate-950/10 dark:border-white/10 dark:bg-slate-950 dark:shadow-black/30 {class}"
>
<div class="mx-auto max-w-7xl px-6 py-8 lg:px-10">
<div class="grid gap-8 lg:grid-cols-[minmax(0,1.65fr)_minmax(18rem,0.7fr)]">
<!-- Main product links -->
<div>
<div class="mb-5 flex items-end justify-between gap-6">
<div>
<p class="text-xs font-semibold uppercase tracking-[0.16em] text-indigo-600 dark:text-indigo-400">
WrNexus platform
</p>
<h2 class="mt-2 text-lg font-bold tracking-tight text-slate-950 dark:text-white">
Everything you need to manage identity
</h2>
</div>
<a
href="/product"
class="group hidden items-center gap-2 text-sm font-semibold text-indigo-600 transition hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300 xl:inline-flex"
>
View all features
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
</div>
<div class="grid gap-2 md:grid-cols-2">
<!-- Platform -->
<a
href="/platform"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-indigo-200 hover:bg-indigo-50 dark:hover:border-indigo-500/20 dark:hover:bg-indigo-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-indigo-100 text-indigo-700 transition group-hover:bg-indigo-600 group-hover:text-white dark:bg-indigo-500/15 dark:text-indigo-300 dark:group-hover:bg-indigo-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--layers-3] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Platform
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-indigo-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-indigo-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Unified identity infrastructure for every application.
</span>
</span>
</a>
<!-- Authentication -->
<a
href="/authentication"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-violet-200 hover:bg-violet-50 dark:hover:border-violet-500/20 dark:hover:bg-violet-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-violet-100 text-violet-700 transition group-hover:bg-violet-600 group-hover:text-white dark:bg-violet-500/15 dark:text-violet-300 dark:group-hover:bg-violet-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--fingerprint] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Authentication
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-violet-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-violet-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Passwords, passkeys, social login, MFA, and SSO.
</span>
</span>
</a>
<!-- User management -->
<a
href="/user-management"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-cyan-200 hover:bg-cyan-50 dark:hover:border-cyan-500/20 dark:hover:bg-cyan-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-cyan-100 text-cyan-700 transition group-hover:bg-cyan-600 group-hover:text-white dark:bg-cyan-500/15 dark:text-cyan-300 dark:group-hover:bg-cyan-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--users-round] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
User management
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-cyan-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-cyan-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Profiles, sessions, invitations, and user lifecycle.
</span>
</span>
</a>
<!-- Organizations -->
<a
href="/organizations"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-emerald-200 hover:bg-emerald-50 dark:hover:border-emerald-500/20 dark:hover:bg-emerald-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-emerald-100 text-emerald-700 transition group-hover:bg-emerald-600 group-hover:text-white dark:bg-emerald-500/15 dark:text-emerald-300 dark:group-hover:bg-emerald-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--building-2] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Organizations
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-emerald-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-emerald-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Multi-tenant workspaces, teams, domains, and membership.
</span>
</span>
</a>
<!-- Authorization -->
<a
href="/authorization"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-amber-200 hover:bg-amber-50 dark:hover:border-amber-500/20 dark:hover:bg-amber-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-amber-100 text-amber-700 transition group-hover:bg-amber-600 group-hover:text-white dark:bg-amber-500/15 dark:text-amber-300 dark:group-hover:bg-amber-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--key-square] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Authorization
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-amber-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-amber-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Roles, permissions, policies, and protected resources.
</span>
</span>
</a>
<!-- Security -->
<a
href="/security"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-rose-200 hover:bg-rose-50 dark:hover:border-rose-500/20 dark:hover:bg-rose-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-rose-100 text-rose-700 transition group-hover:bg-rose-600 group-hover:text-white dark:bg-rose-500/15 dark:text-rose-300 dark:group-hover:bg-rose-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--shield-check] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Security
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-rose-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-rose-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Session controls, recovery, audit logs, and threat protection.
</span>
</span>
</a>
</div>
<a
href="/product"
class="mt-4 flex items-center justify-center gap-2 rounded-xl border border-indigo-200 bg-indigo-50 px-4 py-3 text-sm font-semibold text-indigo-700 transition hover:border-indigo-300 hover:bg-indigo-100 dark:border-indigo-500/20 dark:bg-indigo-500/10 dark:text-indigo-300 dark:hover:border-indigo-500/30 dark:hover:bg-indigo-500/15 xl:hidden"
>
View all product features
<span class="icon-[lucide--arrow-right] h-4 w-4" aria-hidden="true" ></span>
</a>
</div>
<!-- Featured panel -->
<aside
class="relative overflow-hidden rounded-3xl border border-indigo-200 bg-linear-to-br from-indigo-50 via-white to-violet-50 p-6 dark:border-indigo-500/20 dark:from-indigo-500/10 dark:via-slate-900 dark:to-violet-500/10"
>
<div aria-hidden="true" class="absolute -right-16 -top-16 h-40 w-40 rounded-full bg-indigo-300/30 blur-3xl dark:bg-indigo-500/10" ></div>
<div aria-hidden="true" class="absolute -bottom-20 -left-16 h-40 w-40 rounded-full bg-violet-300/30 blur-3xl dark:bg-violet-500/10" ></div>
<div class="relative">
<span
class="inline-flex items-center gap-2 rounded-full border border-indigo-200 bg-white/80 px-3 py-1.5 text-xs font-semibold text-indigo-700 backdrop-blur-sm dark:border-indigo-500/20 dark:bg-white/5 dark:text-indigo-300"
>
<span class="icon-[lucide--sparkles] h-3.5 w-3.5" aria-hidden="true" ></span>
Featured
</span>
<div
class="mt-5 grid h-12 w-12 place-items-center rounded-2xl bg-indigo-600 text-white shadow-lg shadow-indigo-600/20"
>
<span class="icon-[lucide--shield-user] h-6 w-6" aria-hidden="true" ></span>
</div>
<h3 class="mt-5 text-xl font-bold tracking-tight text-slate-950 dark:text-white">
Identity built for modern SaaS
</h3>
<p class="mt-3 text-sm leading-6 text-slate-600 dark:text-slate-400">
Add secure authentication, organizations, permissions, and user
management without rebuilding identity infrastructure.
</p>
<ul class="mt-5 space-y-3">
<li class="flex items-center gap-3 text-sm text-slate-700 dark:text-slate-300">
<span
class="grid h-6 w-6 shrink-0 place-items-center rounded-full bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300"
>
<span class="icon-[lucide--check] h-3.5 w-3.5" aria-hidden="true" ></span>
</span>
Passkeys and multifactor authentication
</li>
<li class="flex items-center gap-3 text-sm text-slate-700 dark:text-slate-300">
<span
class="grid h-6 w-6 shrink-0 place-items-center rounded-full bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300"
>
<span class="icon-[lucide--check] h-3.5 w-3.5" aria-hidden="true" ></span>
</span>
Organizations and workspace isolation
</li>
<li class="flex items-center gap-3 text-sm text-slate-700 dark:text-slate-300">
<span
class="grid h-6 w-6 shrink-0 place-items-center rounded-full bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300"
>
<span class="icon-[lucide--check] h-3.5 w-3.5" aria-hidden="true" ></span>
</span>
APIs and SDKs for every stack
</li>
</ul>
<a
href="/solutions/saas"
class="group mt-6 flex h-11 w-full items-center justify-center gap-2 rounded-xl bg-indigo-600 px-4 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900"
>
Explore SaaS identity
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
<div class="mt-4 flex items-center justify-center gap-2 text-xs text-slate-500 dark:text-slate-400">
<span class="icon-[lucide--code-2] h-4 w-4 text-indigo-600 dark:text-indigo-400" aria-hidden="true" ></span>
Developer-first and API-ready
</div>
</div>
</aside>
</div>
<!-- Bottom quick links -->
<div
class="mt-7 flex flex-wrap items-center justify-between gap-4 border-t border-slate-200 pt-5 dark:border-white/10"
>
<div class="flex flex-wrap items-center gap-x-6 gap-y-3">
<a
href="/integrations"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--plug] h-4 w-4" aria-hidden="true" ></span>
Integrations
</a>
<a
href="/enterprise-sso"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--building-2] h-4 w-4" aria-hidden="true" ></span>
Enterprise SSO
</a>
<a
href="/audit-logs"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--scroll-text] h-4 w-4" aria-hidden="true" ></span>
Audit logs
</a>
</div>
<a
href="/docs"
class="group inline-flex items-center gap-2 text-sm font-semibold text-indigo-600 transition hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300"
>
Read the documentation
<span class="icon-[lucide--arrow-up-right] h-4 w-4 transition-transform group-hover:translate-x-0.5 group-hover:-translate-y-0.5" aria-hidden="true" ></span>
</a>
</div>
</div>
</div>
}
}
+606 -9
View File
@@ -1,11 +1,608 @@
component PublicFooter {
props {
class = ""
brand = "WRNexusJS"
description = "SSR-first, Bun-native full-stack framework."
copyright = "© 2026 WorkRoot Workspace"
}
view {
<footer class="border-t border-slate-200 bg-slate-50 dark:border-slate-800 dark:bg-slate-950 {class}"><div class="mx-auto max-w-7xl px-4 py-14 sm:px-6 lg:px-8"><div class="grid gap-10 lg:grid-cols-[1.2fr_2fr]"><div><div class="text-xl font-black">{brand}</div><p class="mt-3 max-w-sm text-sm leading-6 text-slate-500">{description}</p></div><div class="grid grid-cols-2 gap-8 sm:grid-cols-4"><slot /></div></div><div class="mt-12 flex flex-col gap-4 border-t border-slate-200 pt-6 text-sm text-slate-500 sm:flex-row sm:items-center sm:justify-between dark:border-slate-800"><span>{copyright}</span><button data-wire-theme-toggle type="button" class="min-h-10 rounded-xl border border-slate-300 px-3 dark:border-slate-700">Toggle theme</button></div></div></footer>
}
props {
class: string = ""
}
state email = ""
state newsletterSubmitted = false
view {
<footer
class="relative overflow-hidden border-t border-slate-200 bg-slate-50 dark:border-white/10 dark:bg-slate-950 {class}"
>
<!-- Decorative background -->
<div
aria-hidden="true"
class="pointer-events-none absolute inset-x-0 top-0 h-96 overflow-hidden"
>
<div class="absolute -left-32 -top-48 h-96 w-96 rounded-full bg-indigo-300/20 blur-3xl dark:bg-indigo-500/10"></div>
<div class="absolute -right-32 -top-48 h-96 w-96 rounded-full bg-violet-300/20 blur-3xl dark:bg-violet-500/10"></div>
</div>
<div class="relative w-full px-5 py-12 sm:px-8 lg:px-10 lg:py-16 xl:px-12">
<!-- Newsletter and CTA -->
<section
aria-labelledby="footer-newsletter-title"
class="relative overflow-hidden rounded-3xl border border-indigo-200 bg-linear-to-br from-indigo-50 via-white to-violet-50 p-6 shadow-xl shadow-indigo-950/5 dark:border-indigo-500/20 dark:from-indigo-500/10 dark:via-slate-900 dark:to-violet-500/10 sm:p-8 lg:p-10"
>
<div aria-hidden="true" class="absolute -right-24 -top-24 h-64 w-64 rounded-full bg-indigo-300/30 blur-3xl dark:bg-indigo-500/10" ></div>
<div aria-hidden="true" class="absolute -bottom-24 -left-24 h-64 w-64 rounded-full bg-violet-300/30 blur-3xl dark:bg-violet-500/10" ></div>
<div class="relative grid items-center gap-8 lg:grid-cols-[minmax(0,1fr)_minmax(22rem,0.75fr)]">
<div>
<span
class="inline-flex items-center gap-2 rounded-full border border-indigo-200 bg-white/70 px-3 py-1.5 text-xs font-semibold text-indigo-700 backdrop-blur-sm dark:border-indigo-500/20 dark:bg-white/5 dark:text-indigo-300"
>
<span class="icon-[lucide--mail-plus] h-3.5 w-3.5" aria-hidden="true" ></span>
WrNexus updates
</span>
<h2
id="footer-newsletter-title"
class="mt-5 max-w-2xl text-2xl font-bold tracking-tight text-slate-950 dark:text-white sm:text-3xl"
>
Identity insights delivered to your inbox
</h2>
<p class="mt-3 max-w-2xl text-sm leading-6 text-slate-600 dark:text-slate-400 sm:text-base">
Get product updates, security guidance, implementation
strategies, and practical identity architecture resources.
</p>
</div>
<div>
<form
method="post"
action="/api/newsletter/subscribe"
@submit="newsletterSubmitted = true"
class="rounded-2xl border border-slate-200 bg-white/80 p-2 shadow-lg shadow-slate-950/5 backdrop-blur-sm dark:border-white/10 dark:bg-white/5"
>
<div class="flex flex-col gap-2 sm:flex-row">
<label
class="sr-only"
for="footer-newsletter-email"
>
Work email address
</label>
<div class="relative min-w-0 flex-1">
<span class="icon-[lucide--mail] pointer-events-none absolute left-4 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400" aria-hidden="true" ></span>
<input
id="footer-newsletter-email"
name="email"
type="email"
autocomplete="email"
required
bind:value="email"
placeholder="Enter your work email"
class="h-12 w-full rounded-xl border border-transparent bg-transparent pl-11 pr-4 text-sm text-slate-950 outline-none transition placeholder:text-slate-400 focus:border-indigo-300 focus:bg-white focus:ring-4 focus:ring-indigo-500/10 dark:text-white dark:focus:border-indigo-500/30 dark:focus:bg-slate-900"
/>
</div>
<button
type="submit"
class="group inline-flex h-12 shrink-0 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-5 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900"
>
Subscribe
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</button>
</div>
</form>
<p class="mt-3 flex items-start gap-2 text-xs leading-5 text-slate-500 dark:text-slate-400">
<span class="icon-[lucide--shield-check] mt-0.5 h-3.5 w-3.5 shrink-0" aria-hidden="true" ></span>
No spam. Unsubscribe at any time. Read our
<a
href="/privacy"
class="font-semibold text-indigo-600 hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300"
>
privacy policy
</a>.
</p>
<div
data-show="newsletterSubmitted"
role="status"
class="mt-3 flex items-center gap-2 rounded-xl border border-emerald-200 bg-emerald-50 px-3 py-2.5 text-sm font-medium text-emerald-800 dark:border-emerald-500/20 dark:bg-emerald-500/10 dark:text-emerald-300"
>
<span class="icon-[lucide--circle-check] h-4 w-4 shrink-0" aria-hidden="true" ></span>
Thanks. Please check your inbox to confirm your subscription.
</div>
</div>
</div>
</section>
<!-- Main footer -->
<div class="mt-12 grid gap-10 lg:grid-cols-[minmax(16rem,1.25fr)_minmax(0,3fr)] lg:gap-16">
<!-- Brand -->
<div>
<a
href="/"
aria-label="WrNexus home"
class="group inline-flex items-center gap-3 rounded-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500"
>
<span
class="grid h-11 w-11 place-items-center rounded-2xl bg-indigo-600 text-white shadow-lg shadow-indigo-600/20 transition group-hover:-translate-y-0.5 group-hover:bg-indigo-500"
>
<span class="icon-[lucide--blocks] h-5 w-5" aria-hidden="true" ></span>
</span>
<span>
<span class="block text-lg font-bold tracking-tight text-slate-950 dark:text-white">
WrNexus
</span>
<span
class="block text-[10px] font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400"
>
Identity Cloud
</span>
</span>
</a>
<p class="mt-5 max-w-sm text-sm leading-6 text-slate-600 dark:text-slate-400">
Secure authentication, user management, organizations,
authorization, and enterprise identity for modern applications.
</p>
<div class="mt-6 flex flex-wrap items-center gap-2">
<a
href="/status"
class="inline-flex items-center gap-2 rounded-xl border border-emerald-200 bg-emerald-50 px-3 py-2 text-xs font-semibold text-emerald-800 transition hover:border-emerald-300 hover:bg-emerald-100 dark:border-emerald-500/20 dark:bg-emerald-500/10 dark:text-emerald-300 dark:hover:border-emerald-500/30"
>
<span class="relative flex h-2 w-2">
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-emerald-400 opacity-60"></span>
<span class="relative inline-flex h-2 w-2 rounded-full bg-emerald-500"></span>
</span>
All systems operational
</a>
</div>
<!-- Social links -->
<div class="mt-6 flex items-center gap-2">
<a
href="https://github.com/wrnexus"
target="_blank"
rel="noopener noreferrer"
aria-label="WrNexus on GitHub"
class="grid h-10 w-10 place-items-center rounded-xl border border-slate-200 bg-white text-slate-500 transition hover:border-indigo-300 hover:text-indigo-700 dark:border-white/10 dark:bg-white/5 dark:text-slate-400 dark:hover:border-indigo-500/30 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--github] h-4.5 w-4.5" aria-hidden="true" ></span>
</a>
<a
href="https://www.linkedin.com/company/wrnexus"
target="_blank"
rel="noopener noreferrer"
aria-label="WrNexus on LinkedIn"
class="grid h-10 w-10 place-items-center rounded-xl border border-slate-200 bg-white text-slate-500 transition hover:border-indigo-300 hover:text-indigo-700 dark:border-white/10 dark:bg-white/5 dark:text-slate-400 dark:hover:border-indigo-500/30 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--linkedin] h-4.5 w-4.5" aria-hidden="true" ></span>
</a>
<a
href="https://x.com/wrnexus"
target="_blank"
rel="noopener noreferrer"
aria-label="WrNexus on X"
class="grid h-10 w-10 place-items-center rounded-xl border border-slate-200 bg-white text-slate-500 transition hover:border-indigo-300 hover:text-indigo-700 dark:border-white/10 dark:bg-white/5 dark:text-slate-400 dark:hover:border-indigo-500/30 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--twitter] h-4.5 w-4.5" aria-hidden="true" ></span>
</a>
<a
href="https://www.youtube.com/@wrnexus"
target="_blank"
rel="noopener noreferrer"
aria-label="WrNexus on YouTube"
class="grid h-10 w-10 place-items-center rounded-xl border border-slate-200 bg-white text-slate-500 transition hover:border-indigo-300 hover:text-indigo-700 dark:border-white/10 dark:bg-white/5 dark:text-slate-400 dark:hover:border-indigo-500/30 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--youtube] h-4.5 w-4.5" aria-hidden="true" ></span>
</a>
</div>
</div>
<!-- Navigation columns -->
<nav
aria-label="Footer navigation"
class="grid grid-cols-2 gap-x-6 gap-y-10 sm:grid-cols-3 xl:grid-cols-5"
>
<!-- Product -->
<div>
<h3 class="text-sm font-bold text-slate-950 dark:text-white">
Product
</h3>
<ul class="mt-4 space-y-3">
<li>
<a
href="/platform"
class="footer-link"
>
Platform
</a>
</li>
<li>
<a
href="/authentication"
class="footer-link"
>
Authentication
</a>
</li>
<li>
<a
href="/user-management"
class="footer-link"
>
User management
</a>
</li>
<li>
<a
href="/organizations"
class="footer-link"
>
Organizations
</a>
</li>
<li>
<a
href="/authorization"
class="footer-link"
>
Authorization
</a>
</li>
<li>
<a
href="/enterprise-sso"
class="footer-link"
>
Enterprise SSO
</a>
</li>
</ul>
</div>
<!-- Solutions -->
<div>
<h3 class="text-sm font-bold text-slate-950 dark:text-white">
Solutions
</h3>
<ul class="mt-4 space-y-3">
<li>
<a
href="/solutions/startups"
class="footer-link"
>
Startups
</a>
</li>
<li>
<a
href="/solutions/saas"
class="footer-link"
>
SaaS
</a>
</li>
<li>
<a
href="/solutions/enterprise"
class="footer-link"
>
Enterprise
</a>
</li>
<li>
<a
href="/solutions/developers"
class="footer-link"
>
Developers
</a>
</li>
<li>
<a
href="/solutions/agencies"
class="footer-link"
>
Agencies
</a>
</li>
<li>
<a
href="/solutions/b2b"
class="footer-link"
>
B2B
</a>
</li>
</ul>
</div>
<!-- Developers -->
<div>
<h3 class="text-sm font-bold text-slate-950 dark:text-white">
Developers
</h3>
<ul class="mt-4 space-y-3">
<li>
<a
href="/docs"
class="footer-link"
>
Documentation
</a>
</li>
<li>
<a
href="/docs/api"
class="footer-link"
>
API reference
</a>
</li>
<li>
<a
href="/sdks"
class="footer-link"
>
SDKs
</a>
</li>
<li>
<a
href="/examples"
class="footer-link"
>
Examples
</a>
</li>
<li>
<a
href="/docs/webhooks"
class="footer-link"
>
Webhooks
</a>
</li>
<li>
<a
href="/changelog"
class="footer-link"
>
Changelog
</a>
</li>
</ul>
</div>
<!-- Resources -->
<div>
<h3 class="text-sm font-bold text-slate-950 dark:text-white">
Resources
</h3>
<ul class="mt-4 space-y-3">
<li>
<a
href="/blog"
class="footer-link"
>
Blog
</a>
</li>
<li>
<a
href="/guides"
class="footer-link"
>
Guides
</a>
</li>
<li>
<a
href="/customers"
class="footer-link"
>
Customers
</a>
</li>
<li>
<a
href="/help"
class="footer-link"
>
Help center
</a>
</li>
<li>
<a
href="/security"
class="footer-link"
>
Security
</a>
</li>
<li>
<a
href="/trust"
class="footer-link"
>
Trust center
</a>
</li>
</ul>
</div>
<!-- Company -->
<div>
<h3 class="text-sm font-bold text-slate-950 dark:text-white">
Company
</h3>
<ul class="mt-4 space-y-3">
<li>
<a
href="/about"
class="footer-link"
>
About
</a>
</li>
<li>
<a
href="/careers"
class="footer-link"
>
Careers
</a>
</li>
<li>
<a
href="/partners"
class="footer-link"
>
Partners
</a>
</li>
<li>
<a
href="/contact"
class="footer-link"
>
Contact
</a>
</li>
<li>
<a
href="/pricing"
class="footer-link"
>
Pricing
</a>
</li>
<li>
<a
href="/status"
class="footer-link"
>
Status
</a>
</li>
</ul>
</div>
</nav>
</div>
<!-- Bottom bar -->
<div
class="mt-12 flex flex-col gap-5 border-t border-slate-200 pt-6 dark:border-white/10 lg:flex-row lg:items-center lg:justify-between"
>
<div
class="flex flex-col gap-2 text-xs text-slate-500 dark:text-slate-400 sm:flex-row sm:items-center sm:gap-4"
>
<p>
© 2026 WrNexus. All rights reserved.
</p>
<span class="hidden h-1 w-1 rounded-full bg-slate-300 dark:bg-slate-700 sm:block"></span>
<p>
Built by WorkRoot Workspace.
</p>
</div>
<div class="flex flex-wrap items-center gap-x-5 gap-y-3">
<a
href="/privacy"
class="footer-bottom-link"
>
Privacy
</a>
<a
href="/terms"
class="footer-bottom-link"
>
Terms
</a>
<a
href="/cookies"
class="footer-bottom-link"
>
Cookies
</a>
<a
href="/legal"
class="footer-bottom-link"
>
Legal
</a>
<button
type="button"
data-open-cookie-preferences
class="footer-bottom-link"
>
Cookie preferences
</button>
<button
type="button"
data-wire-theme-toggle
class="inline-flex items-center gap-2 rounded-lg text-xs font-semibold text-slate-500 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--moon] h-3.5 w-3.5 dark:hidden" aria-hidden="true" ></span>
<span class="icon-[lucide--sun] hidden h-3.5 w-3.5 dark:block" aria-hidden="true" ></span>
Theme
</button>
</div>
</div>
</div>
</footer>
}
}
+282 -12
View File
@@ -1,14 +1,284 @@
component PublicHeader {
props {
class = ""
brand = "WRNexusJS"
homeHref = "/"
signInHref = "/signin"
ctaHref = "/start"
ctaLabel = "Get started"
}
state mobileOpen = false
view {
<header class="sticky top-0 z-40 border-b border-slate-200/80 bg-white/85 backdrop-blur-xl dark:border-slate-800 dark:bg-slate-950/85 {class}"><div class="mx-auto flex h-16 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8"><a href="{homeHref}" class="text-lg font-black tracking-tight">{brand}</a><nav class="hidden items-center gap-7 md:flex" aria-label="Primary"><slot /></nav><div class="hidden items-center gap-3 md:flex"><a href="{signInHref}" class="text-sm font-semibold">Sign in</a><a href="{ctaHref}" class="inline-flex min-h-10 items-center rounded-xl bg-violet-600 px-4 text-sm font-semibold text-white">{ctaLabel}</a><button data-wire-theme-toggle type="button" aria-label="Toggle theme" class="size-10 rounded-xl hover:bg-slate-100 dark:hover:bg-slate-800">◐</button></div><button type="button" aria-label="Open menu" aria-expanded="{mobileOpen}" @click="mobileOpen = !mobileOpen" class="size-11 rounded-xl md:hidden">☰</button></div><div data-show="mobileOpen" class="border-t border-slate-200 px-4 py-5 md:hidden dark:border-slate-800"><div class="space-y-3"><slot name="mobile" /></div><div class="mt-5 grid grid-cols-2 gap-3"><a href="{signInHref}" class="inline-flex min-h-11 items-center justify-center rounded-xl border border-slate-300 font-semibold">Sign in</a><a href="{ctaHref}" class="inline-flex min-h-11 items-center justify-center rounded-xl bg-violet-600 font-semibold text-white">{ctaLabel}</a></div></div></header>
}
props {
class: string = ""
}
state activeMenu = ""
state mobileMenuOpen = false
state headerScrolled = false
view {
<header
data-public-header
class="relative sticky top-0 z-50 w-full border-b transition-all duration-300 {class}"
class:border-transparent="!headerScrolled"
class:bg-transparent="!headerScrolled"
class:border-slate-200/80="headerScrolled"
class:bg-white/90="headerScrolled"
class:shadow-sm="headerScrolled"
class:backdrop-blur-xl="headerScrolled"
class:dark:border-white/10="headerScrolled"
class:dark:bg-slate-950/90="headerScrolled"
>
<div
class="relative z-[60] flex h-18 w-full items-center justify-between gap-6 px-5 sm:px-8 lg:px-10 xl:px-12"
>
<!-- Brand -->
<a
href="/"
aria-label="WrNexus home"
class="group inline-flex shrink-0 items-center gap-3 rounded-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-950"
>
<span
class="grid h-10 w-10 place-items-center rounded-xl bg-indigo-600 text-white shadow-lg shadow-indigo-600/20 transition duration-200 group-hover:-translate-y-0.5 group-hover:bg-indigo-500 group-hover:shadow-xl"
>
<span class="icon-[lucide--blocks] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="block text-lg font-bold tracking-tight text-slate-950 dark:text-white">
WrNexus
</span>
<span
class="block text-[10px] font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400"
>
Identity Cloud
</span>
</span>
</a>
<!-- Desktop navigation -->
<nav
aria-label="Main navigation"
class="hidden items-center gap-1 lg:flex"
>
<!-- Product -->
<button
type="button"
@click="activeMenu = activeMenu === 'product' ? '' : 'product'"
aria-haspopup="true"
aria-expanded="{activeMenu === 'product'}"
class="inline-flex h-10 items-center gap-1.5 rounded-xl px-3.5 text-sm font-semibold transition"
class:bg-indigo-50="activeMenu === 'product'"
class:text-indigo-700="activeMenu === 'product'"
class:text-slate-700="activeMenu !== 'product'"
class:hover:bg-slate-100="activeMenu !== 'product'"
class:dark:bg-indigo-500/10="activeMenu === 'product'"
class:dark:text-indigo-300="activeMenu === 'product'"
class:dark:text-slate-300="activeMenu !== 'product'"
class:dark:hover:bg-white/5="activeMenu !== 'product'"
>
Product
<span class="icon-[lucide--chevron-down] h-4 w-4 transition-transform duration-200" class:rotate-180="activeMenu === 'product'" aria-hidden="true" ></span>
</button>
<!-- Solutions -->
<button
type="button"
@click="activeMenu = activeMenu === 'solutions' ? '' : 'solutions'"
aria-haspopup="true"
aria-expanded="{activeMenu === 'solutions'}"
class="group inline-flex h-10 items-center gap-1.5 rounded-xl px-3.5 text-sm font-semibold transition"
class:bg-indigo-50="activeMenu === 'solutions'"
class:text-indigo-700="activeMenu === 'solutions'"
class:text-slate-700="activeMenu !== 'solutions'"
class:hover:bg-slate-100="activeMenu !== 'solutions'"
class:hover:text-slate-950="activeMenu !== 'solutions'"
class:dark:bg-indigo-500/10="activeMenu === 'solutions'"
class:dark:text-indigo-300="activeMenu === 'solutions'"
class:dark:text-slate-300="activeMenu !== 'solutions'"
class:dark:hover:bg-white/5="activeMenu !== 'solutions'"
class:dark:hover:text-white="activeMenu !== 'solutions'"
>
Solutions
<span class="icon-[lucide--chevron-down] h-4 w-4 transition-transform duration-200" class:rotate-180="activeMenu === 'solutions'" aria-hidden="true" ></span>
</button>
<!-- Developers -->
<button
type="button"
@click="activeMenu = activeMenu === 'developers' ? '' : 'developers'"
aria-haspopup="true"
aria-expanded="{activeMenu === 'developers'}"
class="group inline-flex h-10 items-center gap-1.5 rounded-xl px-3.5 text-sm font-semibold transition"
class:bg-indigo-50="activeMenu === 'developers'"
class:text-indigo-700="activeMenu === 'developers'"
class:text-slate-700="activeMenu !== 'developers'"
class:hover:bg-slate-100="activeMenu !== 'developers'"
class:hover:text-slate-950="activeMenu !== 'developers'"
class:dark:bg-indigo-500/10="activeMenu === 'developers'"
class:dark:text-indigo-300="activeMenu === 'developers'"
class:dark:text-slate-300="activeMenu !== 'developers'"
class:dark:hover:bg-white/5="activeMenu !== 'developers'"
class:dark:hover:text-white="activeMenu !== 'developers'"
>
Developers
<span class="icon-[lucide--chevron-down] h-4 w-4 transition-transform duration-200" class:rotate-180="activeMenu === 'developers'" aria-hidden="true" ></span>
</button>
<!-- Resources -->
<button
type="button"
@click="activeMenu = activeMenu === 'resources' ? '' : 'resources'"
aria-haspopup="true"
aria-expanded="{activeMenu === 'resources'}"
class="group inline-flex h-10 items-center gap-1.5 rounded-xl px-3.5 text-sm font-semibold transition"
class:bg-indigo-50="activeMenu === 'resources'"
class:text-indigo-700="activeMenu === 'resources'"
class:text-slate-700="activeMenu !== 'resources'"
class:hover:bg-slate-100="activeMenu !== 'resources'"
class:hover:text-slate-950="activeMenu !== 'resources'"
class:dark:bg-indigo-500/10="activeMenu === 'resources'"
class:dark:text-indigo-300="activeMenu === 'resources'"
class:dark:text-slate-300="activeMenu !== 'resources'"
class:dark:hover:bg-white/5="activeMenu !== 'resources'"
class:dark:hover:text-white="activeMenu !== 'resources'"
>
Resources
<span class="icon-[lucide--chevron-down] h-4 w-4 transition-transform duration-200" class:rotate-180="activeMenu === 'resources'" aria-hidden="true" ></span>
</button>
<a
href="/pricing"
class="inline-flex h-10 items-center rounded-xl px-3.5 text-sm font-semibold text-slate-700 transition hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:text-slate-300 dark:hover:bg-white/5 dark:hover:text-white"
>
Pricing
</a>
</nav>
<!-- Desktop actions -->
<div class="hidden items-center gap-2 lg:flex">
<a
href="/status"
class="mr-1 inline-flex items-center gap-2 rounded-xl px-3 py-2 text-xs font-semibold text-slate-500 transition hover:bg-slate-100 hover:text-slate-950 dark:text-slate-400 dark:hover:bg-white/5 dark:hover:text-white"
>
<span class="relative flex h-2 w-2">
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-emerald-400 opacity-60"></span>
<span class="relative inline-flex h-2 w-2 rounded-full bg-emerald-500"></span>
</span>
All systems operational
</a>
<button
type="button"
data-wire-theme-toggle
aria-label="Toggle color theme"
title="Toggle color theme"
class="grid h-10 w-10 place-items-center rounded-xl border border-slate-200 bg-white text-slate-600 shadow-sm transition hover:border-slate-300 hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-300 dark:hover:border-white/20 dark:hover:bg-white/10 dark:hover:text-white"
>
<span class="icon-[lucide--moon] h-4 w-4 dark:hidden" aria-hidden="true" ></span>
<span class="icon-[lucide--sun] hidden h-4 w-4 dark:block" aria-hidden="true" ></span>
<span class="sr-only">
Toggle color theme
</span>
</button>
<a
href="/sign-in"
class="inline-flex h-10 items-center justify-center rounded-xl px-4 text-sm font-semibold text-slate-700 transition hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:text-slate-200 dark:hover:bg-white/5 dark:hover:text-white"
>
Sign in
</a>
<a
href="/sign-up"
class="group inline-flex h-10 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-4 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-950"
>
Start free
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-0.5" aria-hidden="true" ></span>
</a>
</div>
<!-- Mobile actions -->
<div class="flex items-center gap-2 lg:hidden">
<button
type="button"
data-wire-theme-toggle
aria-label="Toggle color theme"
title="Toggle color theme"
class="grid h-10 w-10 place-items-center rounded-xl border border-slate-200 bg-white text-slate-600 shadow-sm transition hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-300 dark:hover:bg-white/10 dark:hover:text-white"
>
<span class="icon-[lucide--moon] h-4 w-4 dark:hidden" aria-hidden="true" ></span>
<span class="icon-[lucide--sun] hidden h-4 w-4 dark:block" aria-hidden="true" ></span>
</button>
<button
type="button"
@click="mobileMenuOpen = !mobileMenuOpen"
aria-label="{mobileMenuOpen ? 'Close navigation menu' : 'Open navigation menu'}"
aria-expanded="{mobileMenuOpen}"
aria-controls="public-mobile-navigation"
class="grid h-10 w-10 place-items-center rounded-xl border border-slate-200 bg-white text-slate-700 shadow-sm transition hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-200 dark:hover:bg-white/10 dark:hover:text-white"
>
<span data-show="!mobileMenuOpen" class="icon-[lucide--menu] h-5 w-5" aria-hidden="true" ></span>
<span data-show="mobileMenuOpen" class="icon-[lucide--x] h-5 w-5" aria-hidden="true" ></span>
<span class="sr-only">
Toggle navigation
</span>
</button>
</div>
</div>
<!-- Outside-click backdrop -->
<button type="button" data-show="activeMenu !== ''" @click="activeMenu = ''" aria-label="Close navigation menu" class="fixed inset-0 z-40 hidden cursor-default bg-transparent lg:block" class:hidden="activeMenu === ''" class:block="activeMenu !== ''" ></button>
<!-- Desktop mega menus -->
<div
class="absolute inset-x-0 top-full z-50 max-lg:hidden"
class:hidden="activeMenu === ''"
class:block="activeMenu !== ''"
>
<div
class:hidden="activeMenu !== 'product'"
class:block="activeMenu === 'product'"
>
<ProductMegaMenu />
</div>
<div
class:hidden="activeMenu !== 'solutions'"
class:block="activeMenu === 'solutions'"
>
<SolutionsMegaMenu />
</div>
<div
class:hidden="activeMenu !== 'developers'"
class:block="activeMenu === 'developers'"
>
<DevelopersMegaMenu />
</div>
<div
class:hidden="activeMenu !== 'resources'"
class:block="activeMenu === 'resources'"
>
<ResourcesMegaMenu />
</div>
</div>
<!-- Mobile navigation -->
<div
id="public-mobile-navigation"
class="lg:hidden"
class:hidden="!mobileMenuOpen"
class:block="mobileMenuOpen"
>
<PublicMobileNavigation />
</div>
</header>
}
}
@@ -0,0 +1,589 @@
component PublicMobileNavigation {
props {
class: string = ""
}
state openSection = ""
view {
<div
class="border-t border-slate-200 bg-white shadow-xl dark:border-white/10 dark:bg-slate-950 lg:hidden {class}"
>
<div class="mx-auto max-h-[calc(100dvh-4.5rem)] max-w-7xl overflow-y-auto px-5 py-5 sm:px-8">
<!-- Mobile status -->
<a
href="/status"
class="mb-5 flex items-center justify-between rounded-2xl border border-emerald-200 bg-emerald-50 px-4 py-3 transition hover:border-emerald-300 hover:bg-emerald-100 dark:border-emerald-500/20 dark:bg-emerald-500/10 dark:hover:border-emerald-500/30 dark:hover:bg-emerald-500/15"
>
<span class="flex items-center gap-3">
<span class="relative flex h-2.5 w-2.5 shrink-0">
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-emerald-400 opacity-60"></span>
<span class="relative inline-flex h-2.5 w-2.5 rounded-full bg-emerald-500"></span>
</span>
<span>
<span class="block text-sm font-semibold text-emerald-900 dark:text-emerald-100">
All systems operational
</span>
<span class="mt-0.5 block text-xs text-emerald-700 dark:text-emerald-300">
View service status
</span>
</span>
</span>
<span class="icon-[lucide--arrow-up-right] h-4 w-4 shrink-0 text-emerald-700 dark:text-emerald-300" aria-hidden="true" ></span>
</a>
<nav
aria-label="Mobile navigation"
class="space-y-2"
>
<!-- Product -->
<div
class="overflow-hidden rounded-2xl border border-slate-200 bg-white dark:border-white/10 dark:bg-white/5"
>
<button
type="button"
@click="openSection = openSection === 'product' ? '' : 'product'"
aria-expanded="{openSection === 'product'}"
aria-controls="mobile-product-menu"
class="flex w-full items-center justify-between gap-4 px-4 py-4 text-left transition hover:bg-slate-50 dark:hover:bg-white/5"
class:bg-indigo-50="openSection === 'product'"
class:dark:bg-indigo-500/10="openSection === 'product'"
>
<span class="flex items-center gap-3">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl transition"
class:bg-indigo-100="openSection === 'product'"
class:text-indigo-700="openSection === 'product'"
class:bg-slate-100="openSection !== 'product'"
class:text-slate-600="openSection !== 'product'"
class:dark:bg-indigo-500/15="openSection === 'product'"
class:dark:text-indigo-300="openSection === 'product'"
class:dark:bg-white/5="openSection !== 'product'"
class:dark:text-slate-300="openSection !== 'product'"
>
<span class="icon-[lucide--boxes] h-5 w-5" aria-hidden="true" ></span>
</span>
<span>
<span
class="block text-sm font-semibold"
class:text-indigo-950="openSection === 'product'"
class:text-slate-950="openSection !== 'product'"
class:dark:text-indigo-100="openSection === 'product'"
class:dark:text-white="openSection !== 'product'"
>
Product
</span>
<span class="mt-0.5 block text-xs text-slate-500 dark:text-slate-400">
Explore the WrNexus platform
</span>
</span>
</span>
<span class="icon-[lucide--chevron-down] h-5 w-5 shrink-0 text-slate-400 transition-transform duration-200" class:rotate-180="openSection === 'product'" aria-hidden="true" ></span>
</button>
<div
id="mobile-product-menu"
data-show="openSection === 'product'"
class="border-t border-slate-200 px-3 py-3 dark:border-white/10"
>
<div class="grid gap-1">
<a
href="/platform"
class="group flex items-start gap-3 rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span
class="grid h-9 w-9 shrink-0 place-items-center rounded-lg bg-indigo-50 text-indigo-700 dark:bg-indigo-500/10 dark:text-indigo-300"
>
<span class="icon-[lucide--layers-3] h-4 w-4" aria-hidden="true" ></span>
</span>
<span>
<span
class="block text-sm font-semibold text-slate-900 group-hover:text-indigo-700 dark:text-white dark:group-hover:text-indigo-300"
>
Platform
</span>
<span class="mt-0.5 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Complete identity infrastructure
</span>
</span>
</a>
<a
href="/authentication"
class="group flex items-start gap-3 rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span
class="grid h-9 w-9 shrink-0 place-items-center rounded-lg bg-violet-50 text-violet-700 dark:bg-violet-500/10 dark:text-violet-300"
>
<span class="icon-[lucide--fingerprint] h-4 w-4" aria-hidden="true" ></span>
</span>
<span>
<span
class="block text-sm font-semibold text-slate-900 group-hover:text-indigo-700 dark:text-white dark:group-hover:text-indigo-300"
>
Authentication
</span>
<span class="mt-0.5 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Passwords, passkeys, social login, and SSO
</span>
</span>
</a>
<a
href="/user-management"
class="group flex items-start gap-3 rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span
class="grid h-9 w-9 shrink-0 place-items-center rounded-lg bg-cyan-50 text-cyan-700 dark:bg-cyan-500/10 dark:text-cyan-300"
>
<span class="icon-[lucide--users-round] h-4 w-4" aria-hidden="true" ></span>
</span>
<span>
<span
class="block text-sm font-semibold text-slate-900 group-hover:text-indigo-700 dark:text-white dark:group-hover:text-indigo-300"
>
User management
</span>
<span class="mt-0.5 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Profiles, sessions, invitations, and lifecycle
</span>
</span>
</a>
<a
href="/organizations"
class="group flex items-start gap-3 rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span
class="grid h-9 w-9 shrink-0 place-items-center rounded-lg bg-emerald-50 text-emerald-700 dark:bg-emerald-500/10 dark:text-emerald-300"
>
<span class="icon-[lucide--building-2] h-4 w-4" aria-hidden="true" ></span>
</span>
<span>
<span
class="block text-sm font-semibold text-slate-900 group-hover:text-indigo-700 dark:text-white dark:group-hover:text-indigo-300"
>
Organizations
</span>
<span class="mt-0.5 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Multi-tenant workspaces and teams
</span>
</span>
</a>
<a
href="/security"
class="group flex items-start gap-3 rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span
class="grid h-9 w-9 shrink-0 place-items-center rounded-lg bg-amber-50 text-amber-700 dark:bg-amber-500/10 dark:text-amber-300"
>
<span class="icon-[lucide--shield-check] h-4 w-4" aria-hidden="true" ></span>
</span>
<span>
<span
class="block text-sm font-semibold text-slate-900 group-hover:text-indigo-700 dark:text-white dark:group-hover:text-indigo-300"
>
Security
</span>
<span class="mt-0.5 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Protect identities and sensitive actions
</span>
</span>
</a>
<a
href="/integrations"
class="group flex items-start gap-3 rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span
class="grid h-9 w-9 shrink-0 place-items-center rounded-lg bg-rose-50 text-rose-700 dark:bg-rose-500/10 dark:text-rose-300"
>
<span class="icon-[lucide--plug] h-4 w-4" aria-hidden="true" ></span>
</span>
<span>
<span
class="block text-sm font-semibold text-slate-900 group-hover:text-indigo-700 dark:text-white dark:group-hover:text-indigo-300"
>
Integrations
</span>
<span class="mt-0.5 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Connect your stack and applications
</span>
</span>
</a>
</div>
<a
href="/product"
class="mt-2 flex items-center justify-center gap-2 rounded-xl bg-indigo-50 px-4 py-3 text-sm font-semibold text-indigo-700 transition hover:bg-indigo-100 dark:bg-indigo-500/10 dark:text-indigo-300 dark:hover:bg-indigo-500/15"
>
View all product features
<span class="icon-[lucide--arrow-right] h-4 w-4" aria-hidden="true" ></span>
</a>
</div>
</div>
<!-- Solutions -->
<div
class="overflow-hidden rounded-2xl border border-slate-200 bg-white dark:border-white/10 dark:bg-white/5"
>
<button
type="button"
@click="openSection = openSection === 'solutions' ? '' : 'solutions'"
aria-expanded="{openSection === 'solutions'}"
aria-controls="mobile-solutions-menu"
class="flex w-full items-center justify-between gap-4 px-4 py-4 text-left transition hover:bg-slate-50 dark:hover:bg-white/5"
class:bg-indigo-50="openSection === 'solutions'"
class:dark:bg-indigo-500/10="openSection === 'solutions'"
>
<span class="flex items-center gap-3">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl transition"
class:bg-indigo-100="openSection === 'solutions'"
class:text-indigo-700="openSection === 'solutions'"
class:bg-slate-100="openSection !== 'solutions'"
class:text-slate-600="openSection !== 'solutions'"
class:dark:bg-indigo-500/15="openSection === 'solutions'"
class:dark:text-indigo-300="openSection === 'solutions'"
class:dark:bg-white/5="openSection !== 'solutions'"
class:dark:text-slate-300="openSection !== 'solutions'"
>
<span class="icon-[lucide--lightbulb] h-5 w-5" aria-hidden="true" ></span>
</span>
<span>
<span class="block text-sm font-semibold text-slate-950 dark:text-white">
Solutions
</span>
<span class="mt-0.5 block text-xs text-slate-500 dark:text-slate-400">
Built for your business
</span>
</span>
</span>
<span class="icon-[lucide--chevron-down] h-5 w-5 shrink-0 text-slate-400 transition-transform duration-200" class:rotate-180="openSection === 'solutions'" aria-hidden="true" ></span>
</button>
<div
id="mobile-solutions-menu"
data-show="openSection === 'solutions'"
class="border-t border-slate-200 px-3 py-3 dark:border-white/10"
>
<div class="grid grid-cols-2 gap-2">
<a
href="/solutions/startups"
class="rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span class="icon-[lucide--rocket] h-5 w-5 text-indigo-600 dark:text-indigo-400"></span>
<span class="mt-2 block text-sm font-semibold text-slate-900 dark:text-white">
Startups
</span>
</a>
<a
href="/solutions/saas"
class="rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span class="icon-[lucide--cloud] h-5 w-5 text-cyan-600 dark:text-cyan-400"></span>
<span class="mt-2 block text-sm font-semibold text-slate-900 dark:text-white">
SaaS
</span>
</a>
<a
href="/solutions/enterprise"
class="rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span class="icon-[lucide--building] h-5 w-5 text-violet-600 dark:text-violet-400"></span>
<span class="mt-2 block text-sm font-semibold text-slate-900 dark:text-white">
Enterprise
</span>
</a>
<a
href="/solutions/developers"
class="rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span class="icon-[lucide--code-2] h-5 w-5 text-emerald-600 dark:text-emerald-400"></span>
<span class="mt-2 block text-sm font-semibold text-slate-900 dark:text-white">
Developers
</span>
</a>
<a
href="/solutions/agencies"
class="rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span class="icon-[lucide--briefcase-business] h-5 w-5 text-amber-600 dark:text-amber-400"></span>
<span class="mt-2 block text-sm font-semibold text-slate-900 dark:text-white">
Agencies
</span>
</a>
<a
href="/solutions/b2b"
class="rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span class="icon-[lucide--handshake] h-5 w-5 text-rose-600 dark:text-rose-400"></span>
<span class="mt-2 block text-sm font-semibold text-slate-900 dark:text-white">
B2B
</span>
</a>
</div>
</div>
</div>
<!-- Developers -->
<div
class="overflow-hidden rounded-2xl border border-slate-200 bg-white dark:border-white/10 dark:bg-white/5"
>
<button
type="button"
@click="openSection = openSection === 'developers' ? '' : 'developers'"
aria-expanded="{openSection === 'developers'}"
aria-controls="mobile-developers-menu"
class="flex w-full items-center justify-between gap-4 px-4 py-4 text-left transition hover:bg-slate-50 dark:hover:bg-white/5"
class:bg-indigo-50="openSection === 'developers'"
class:dark:bg-indigo-500/10="openSection === 'developers'"
>
<span class="flex items-center gap-3">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl transition"
class:bg-indigo-100="openSection === 'developers'"
class:text-indigo-700="openSection === 'developers'"
class:bg-slate-100="openSection !== 'developers'"
class:text-slate-600="openSection !== 'developers'"
class:dark:bg-indigo-500/15="openSection === 'developers'"
class:dark:text-indigo-300="openSection === 'developers'"
class:dark:bg-white/5="openSection !== 'developers'"
class:dark:text-slate-300="openSection !== 'developers'"
>
<span class="icon-[lucide--terminal-square] h-5 w-5" aria-hidden="true" ></span>
</span>
<span>
<span class="block text-sm font-semibold text-slate-950 dark:text-white">
Developers
</span>
<span class="mt-0.5 block text-xs text-slate-500 dark:text-slate-400">
Build with WrNexus
</span>
</span>
</span>
<span class="icon-[lucide--chevron-down] h-5 w-5 shrink-0 text-slate-400 transition-transform duration-200" class:rotate-180="openSection === 'developers'" aria-hidden="true" ></span>
</button>
<div
id="mobile-developers-menu"
data-show="openSection === 'developers'"
class="border-t border-slate-200 px-3 py-3 dark:border-white/10"
>
<div class="grid gap-1">
<a
href="/docs"
class="flex items-center gap-3 rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 hover:text-indigo-700 dark:text-slate-200 dark:hover:bg-white/5 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--book-open] h-5 w-5 text-indigo-600 dark:text-indigo-400"></span>
Documentation
</a>
<a
href="/docs/api"
class="flex items-center gap-3 rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 hover:text-indigo-700 dark:text-slate-200 dark:hover:bg-white/5 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--braces] h-5 w-5 text-violet-600 dark:text-violet-400"></span>
API reference
</a>
<a
href="/sdks"
class="flex items-center gap-3 rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 hover:text-indigo-700 dark:text-slate-200 dark:hover:bg-white/5 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--package] h-5 w-5 text-cyan-600 dark:text-cyan-400"></span>
SDKs
</a>
<a
href="/examples"
class="flex items-center gap-3 rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 hover:text-indigo-700 dark:text-slate-200 dark:hover:bg-white/5 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--flask-conical] h-5 w-5 text-emerald-600 dark:text-emerald-400"></span>
Examples
</a>
<a
href="/changelog"
class="flex items-center gap-3 rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 hover:text-indigo-700 dark:text-slate-200 dark:hover:bg-white/5 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--history] h-5 w-5 text-amber-600 dark:text-amber-400"></span>
Changelog
</a>
</div>
</div>
</div>
<!-- Resources -->
<div
class="overflow-hidden rounded-2xl border border-slate-200 bg-white dark:border-white/10 dark:bg-white/5"
>
<button
type="button"
@click="openSection = openSection === 'resources' ? '' : 'resources'"
aria-expanded="{openSection === 'resources'}"
aria-controls="mobile-resources-menu"
class="flex w-full items-center justify-between gap-4 px-4 py-4 text-left transition hover:bg-slate-50 dark:hover:bg-white/5"
class:bg-indigo-50="openSection === 'resources'"
class:dark:bg-indigo-500/10="openSection === 'resources'"
>
<span class="flex items-center gap-3">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl transition"
class:bg-indigo-100="openSection === 'resources'"
class:text-indigo-700="openSection === 'resources'"
class:bg-slate-100="openSection !== 'resources'"
class:text-slate-600="openSection !== 'resources'"
class:dark:bg-indigo-500/15="openSection === 'resources'"
class:dark:text-indigo-300="openSection === 'resources'"
class:dark:bg-white/5="openSection !== 'resources'"
class:dark:text-slate-300="openSection !== 'resources'"
>
<span class="icon-[lucide--library] h-5 w-5" aria-hidden="true" ></span>
</span>
<span>
<span class="block text-sm font-semibold text-slate-950 dark:text-white">
Resources
</span>
<span class="mt-0.5 block text-xs text-slate-500 dark:text-slate-400">
Learn and get support
</span>
</span>
</span>
<span class="icon-[lucide--chevron-down] h-5 w-5 shrink-0 text-slate-400 transition-transform duration-200" class:rotate-180="openSection === 'resources'" aria-hidden="true" ></span>
</button>
<div
id="mobile-resources-menu"
data-show="openSection === 'resources'"
class="border-t border-slate-200 px-3 py-3 dark:border-white/10"
>
<div class="grid grid-cols-2 gap-2">
<a
href="/blog"
class="rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-white/5"
>
Blog
</a>
<a
href="/customers"
class="rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-white/5"
>
Customer stories
</a>
<a
href="/guides"
class="rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-white/5"
>
Guides
</a>
<a
href="/help"
class="rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-white/5"
>
Help center
</a>
<a
href="/security"
class="rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-white/5"
>
Security
</a>
<a
href="/trust"
class="rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-white/5"
>
Trust center
</a>
</div>
</div>
</div>
<!-- Direct links -->
<a
href="/pricing"
class="flex items-center justify-between rounded-2xl border border-slate-200 bg-white px-4 py-4 text-sm font-semibold text-slate-950 transition hover:border-indigo-300 hover:bg-indigo-50 dark:border-white/10 dark:bg-white/5 dark:text-white dark:hover:border-indigo-500 dark:hover:bg-indigo-500/10"
>
<span class="flex items-center gap-3">
<span
class="grid h-10 w-10 place-items-center rounded-xl bg-slate-100 text-slate-600 dark:bg-white/5 dark:text-slate-300"
>
<span class="icon-[lucide--badge-dollar-sign] h-5 w-5" aria-hidden="true" ></span>
</span>
Pricing
</span>
<span class="icon-[lucide--chevron-right] h-5 w-5 text-slate-400" aria-hidden="true" ></span>
</a>
</nav>
<!-- Mobile CTAs -->
<div class="mt-5 grid gap-3 border-t border-slate-200 pt-5 dark:border-white/10 sm:grid-cols-2">
<a
href="/sign-in"
class="flex h-12 items-center justify-center gap-2 rounded-xl border border-slate-200 bg-white px-5 text-sm font-semibold text-slate-700 shadow-sm transition hover:border-indigo-300 hover:bg-slate-50 hover:text-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-200 dark:hover:border-indigo-500 dark:hover:bg-white/10 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--log-in] h-4 w-4" aria-hidden="true" ></span>
Sign in
</a>
<a
href="/sign-up"
class="group flex h-12 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-5 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500"
>
Start free
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
</div>
<p class="mt-5 text-center text-xs leading-5 text-slate-400 dark:text-slate-500">
Secure identity infrastructure for modern applications.
</p>
</div>
</div>
}
}
+125
View File
@@ -0,0 +1,125 @@
component PublicSearch {
props {
class: string = ""
query = ""
placeholder = "Search..."
label = "Search"
action = ""
method = "get"
name = "q"
buttonLabel = ""
clearLabel = "Clear search"
size = "default"
centered = false
fullWidth = false
showShortcut = false
shortcutLabel = "⌘ K"
suggestions = []
}
state searchValue = query
state focused = false
view {
<div
class="w-full {class}"
class:max-w-2xl="!fullWidth"
class:mx-auto="centered"
>
<form
action="{action}"
method="{method}"
role="search"
class="w-full"
>
<label
for="public-search-input"
class="sr-only"
>
{label}
</label>
<div
class=" group relative flex w-full items-center rounded-2xl border bg-white shadow-sm transition-all duration-200 dark:bg-white/5 {focused ? 'border-indigo-400 ring-4 ring-indigo-500/10 dark:border-indigo-500/50 dark:ring-indigo-500/10' : 'border-slate-200 hover:border-slate-300 dark:border-white/10 dark:hover:border-white/20' } {size === 'compact' ? 'min-h-11' : size === 'large' ? 'min-h-14' : 'min-h-12' } "
>
<!-- Search icon -->
<span class=" pointer-events-none absolute left-4 text-slate-400 transition group-focus-within:text-indigo-600 dark:text-slate-500 dark:group-focus-within:text-indigo-400 {size === 'large' ? 'h-5 w-5' : 'h-4.5 w-4.5'} icon-[lucide--search] " aria-hidden="true" ></span>
<!-- Input -->
<input
id="public-search-input"
type="search"
name="{name}"
value="{searchValue}"
placeholder="{placeholder}"
autocomplete="off"
@input="searchValue = event.target.value"
@focus="focused = true"
@blur="focused = false"
class=" min-w-0 flex-1 bg-transparent text-slate-950 outline-none placeholder:text-slate-400 dark:text-white dark:placeholder:text-slate-500 {size === 'compact' ? 'h-11 pl-11 pr-3 text-sm' : size === 'large' ? 'h-14 pl-12 pr-4 text-base' : 'h-12 pl-11 pr-4 text-sm' } "
/>
<!-- Keyboard shortcut -->
<span
class="mr-3 hidden shrink-0 items-center rounded-md border border-slate-200 bg-slate-50 px-2 py-1 text-[11px] font-semibold text-slate-400 dark:border-white/10 dark:bg-white/5 dark:text-slate-500 sm:inline-flex"
class:hidden="!showShortcut || searchValue !== ''"
>
{shortcutLabel}
</span>
<!-- Clear button -->
<button
type="button"
@click="searchValue = ''"
aria-label="{clearLabel}"
class="mr-2 grid h-8 w-8 shrink-0 place-items-center rounded-lg text-slate-400 transition hover:bg-slate-100 hover:text-slate-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:text-slate-500 dark:hover:bg-white/10 dark:hover:text-slate-200"
class:hidden="searchValue === ''"
>
<span class="icon-[lucide--x] h-4 w-4" aria-hidden="true" ></span>
</button>
<!-- Submit button -->
<button
type="submit"
class=" mr-1.5 shrink-0 items-center justify-center gap-2 rounded-xl bg-indigo-600 font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900 {size === 'compact' ? 'h-9 px-3 text-xs' : size === 'large' ? 'h-11 px-5 text-sm' : 'h-10 px-4 text-sm' } "
class:hidden="buttonLabel === ''"
class:inline-flex="buttonLabel !== ''"
>
{buttonLabel}
<span class="icon-[lucide--arrow-right] h-4 w-4" aria-hidden="true" ></span>
</button>
</div>
</form>
<!-- Suggested searches -->
<div
class="mt-4 flex flex-wrap items-center gap-2"
class:hidden="suggestions.length === 0"
class:justify-center="centered"
class:justify-start="!centered"
>
<span class="mr-1 text-xs font-medium text-slate-500 dark:text-slate-400">
Popular:
</span>
{#each suggestions.slice(0, 5) as suggestion}
<a
href="{suggestion.href}"
class="inline-flex items-center gap-1.5 rounded-full border border-slate-200 bg-white px-3 py-1.5 text-xs font-semibold text-slate-600 shadow-sm transition hover:-translate-y-0.5 hover:border-indigo-300 hover:bg-indigo-50 hover:text-indigo-700 dark:border-white/10 dark:bg-white/5 dark:text-slate-300 dark:hover:border-indigo-500/30 dark:hover:bg-indigo-500/10 dark:hover:text-indigo-300"
>
<span class="{suggestion.iconClass} h-3.5 w-3.5" aria-hidden="true" class:hidden="suggestion.iconClass === ''" ></span>
{suggestion.label}
</a>
{/each}
</div>
</div>
}
}
+363 -8
View File
@@ -1,10 +1,365 @@
component ResourcesMegaMenu {
props {
title = ""
description = ""
open = false
closeLabel = "Close"
class = ""
}
view { <div data-show="open" class="wire-overlay wire-catalog-overlay {class}"><section role="dialog" aria-modal="true" aria-label="{title}" class="wire-dialog"><header><h2 data-show="title" class="wire-dialog__title">{title}</h2><p data-show="description" class="wire-dialog__description">{description}</p></header><slot /><button type="button" class="wire-btn wire-btn--ghost">{closeLabel}</button></section></div> }
props {
class: string = ""
}
view {
<div
class="border-t border-slate-200 bg-white shadow-2xl shadow-slate-950/10 dark:border-white/10 dark:bg-slate-950 dark:shadow-black/30 {class}"
>
<div class="mx-auto max-w-7xl px-6 py-8 lg:px-10">
<div class="grid gap-8 lg:grid-cols-[minmax(0,1.55fr)_minmax(18rem,0.8fr)]">
<!-- Resource links -->
<section>
<div class="mb-5 flex items-end justify-between gap-6">
<div>
<p class="text-xs font-semibold uppercase tracking-[0.16em] text-indigo-600 dark:text-indigo-400">
Resources
</p>
<h2 class="mt-2 text-lg font-bold tracking-tight text-slate-950 dark:text-white">
Learn, build, and get support
</h2>
<p class="mt-1 text-sm text-slate-500 dark:text-slate-400">
Explore practical guides, customer stories, product updates,
and security resources.
</p>
</div>
<a
href="/resources"
class="group hidden items-center gap-2 text-sm font-semibold text-indigo-600 transition hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300 xl:inline-flex"
>
View all resources
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
</div>
<div class="grid gap-2 md:grid-cols-2">
<!-- Blog -->
<a
href="/blog"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-indigo-200 hover:bg-indigo-50 dark:hover:border-indigo-500/20 dark:hover:bg-indigo-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-indigo-100 text-indigo-700 transition group-hover:bg-indigo-600 group-hover:text-white dark:bg-indigo-500/15 dark:text-indigo-300 dark:group-hover:bg-indigo-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--newspaper] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Blog
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-indigo-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-indigo-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Product updates, engineering insights, identity trends, and
company news.
</span>
</span>
</a>
<!-- Customer stories -->
<a
href="/customers"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-violet-200 hover:bg-violet-50 dark:hover:border-violet-500/20 dark:hover:bg-violet-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-violet-100 text-violet-700 transition group-hover:bg-violet-600 group-hover:text-white dark:bg-violet-500/15 dark:text-violet-300 dark:group-hover:bg-violet-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--users-round] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Customer stories
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-violet-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-violet-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
See how teams use WrNexus to secure and scale their
applications.
</span>
</span>
</a>
<!-- Guides -->
<a
href="/guides"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-cyan-200 hover:bg-cyan-50 dark:hover:border-cyan-500/20 dark:hover:bg-cyan-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-cyan-100 text-cyan-700 transition group-hover:bg-cyan-600 group-hover:text-white dark:bg-cyan-500/15 dark:text-cyan-300 dark:group-hover:bg-cyan-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--book-marked] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Guides
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-cyan-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-cyan-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Practical identity, security, SaaS, and architecture
walkthroughs.
</span>
</span>
</a>
<!-- Help center -->
<a
href="/help"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-emerald-200 hover:bg-emerald-50 dark:hover:border-emerald-500/20 dark:hover:bg-emerald-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-emerald-100 text-emerald-700 transition group-hover:bg-emerald-600 group-hover:text-white dark:bg-emerald-500/15 dark:text-emerald-300 dark:group-hover:bg-emerald-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--circle-help] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Help center
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-emerald-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-emerald-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Find answers for account, workspace, billing, and security
questions.
</span>
</span>
</a>
<!-- Security -->
<a
href="/security"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-amber-200 hover:bg-amber-50 dark:hover:border-amber-500/20 dark:hover:bg-amber-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-amber-100 text-amber-700 transition group-hover:bg-amber-600 group-hover:text-white dark:bg-amber-500/15 dark:text-amber-300 dark:group-hover:bg-amber-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--shield-check] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Security
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-amber-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-amber-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Learn how WrNexus protects accounts, sessions, and
organizations.
</span>
</span>
</a>
<!-- Trust center -->
<a
href="/trust"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-rose-200 hover:bg-rose-50 dark:hover:border-rose-500/20 dark:hover:bg-rose-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-rose-100 text-rose-700 transition group-hover:bg-rose-600 group-hover:text-white dark:bg-rose-500/15 dark:text-rose-300 dark:group-hover:bg-rose-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--badge-check] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Trust center
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-rose-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-rose-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Review compliance, privacy, infrastructure, and operational
controls.
</span>
</span>
</a>
</div>
<a
href="/resources"
class="mt-4 flex items-center justify-center gap-2 rounded-xl border border-indigo-200 bg-indigo-50 px-4 py-3 text-sm font-semibold text-indigo-700 transition hover:border-indigo-300 hover:bg-indigo-100 dark:border-indigo-500/20 dark:bg-indigo-500/10 dark:text-indigo-300 dark:hover:border-indigo-500/30 dark:hover:bg-indigo-500/15 xl:hidden"
>
View all resources
<span class="icon-[lucide--arrow-right] h-4 w-4" aria-hidden="true" ></span>
</a>
</section>
<!-- Featured resource -->
<aside
class="relative overflow-hidden rounded-3xl border border-indigo-200 bg-linear-to-br from-indigo-50 via-white to-cyan-50 p-6 dark:border-indigo-500/20 dark:from-indigo-500/10 dark:via-slate-900 dark:to-cyan-500/10"
>
<div aria-hidden="true" class="absolute -right-16 -top-16 h-40 w-40 rounded-full bg-indigo-300/30 blur-3xl dark:bg-indigo-500/10" ></div>
<div aria-hidden="true" class="absolute -bottom-20 -left-16 h-40 w-40 rounded-full bg-cyan-300/30 blur-3xl dark:bg-cyan-500/10" ></div>
<div class="relative">
<span
class="inline-flex items-center gap-2 rounded-full border border-indigo-200 bg-white/80 px-3 py-1.5 text-xs font-semibold text-indigo-700 backdrop-blur-sm dark:border-indigo-500/20 dark:bg-white/5 dark:text-indigo-300"
>
<span class="icon-[lucide--sparkles] h-3.5 w-3.5" aria-hidden="true" ></span>
Featured guide
</span>
<div
class="mt-5 grid h-12 w-12 place-items-center rounded-2xl bg-indigo-600 text-white shadow-lg shadow-indigo-600/20"
>
<span class="icon-[lucide--book-open-check] h-6 w-6" aria-hidden="true" ></span>
</div>
<h3 class="mt-5 text-xl font-bold tracking-tight text-slate-950 dark:text-white">
The modern SaaS identity checklist
</h3>
<p class="mt-3 text-sm leading-6 text-slate-600 dark:text-slate-400">
A practical checklist covering authentication, organizations,
permissions, MFA, recovery, sessions, and enterprise SSO.
</p>
<div class="mt-5 space-y-3">
<div class="flex items-center gap-3">
<span
class="grid h-7 w-7 shrink-0 place-items-center rounded-full bg-indigo-100 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300"
>
<span class="icon-[lucide--check] h-4 w-4" aria-hidden="true" ></span>
</span>
<span class="text-sm text-slate-700 dark:text-slate-300">
Secure authentication foundations
</span>
</div>
<div class="flex items-center gap-3">
<span
class="grid h-7 w-7 shrink-0 place-items-center rounded-full bg-indigo-100 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300"
>
<span class="icon-[lucide--check] h-4 w-4" aria-hidden="true" ></span>
</span>
<span class="text-sm text-slate-700 dark:text-slate-300">
Multi-tenant organization design
</span>
</div>
<div class="flex items-center gap-3">
<span
class="grid h-7 w-7 shrink-0 place-items-center rounded-full bg-indigo-100 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300"
>
<span class="icon-[lucide--check] h-4 w-4" aria-hidden="true" ></span>
</span>
<span class="text-sm text-slate-700 dark:text-slate-300">
Recovery and account protection
</span>
</div>
</div>
<a
href="/guides/saas-identity-checklist"
class="group mt-6 flex h-11 w-full items-center justify-center gap-2 rounded-xl bg-indigo-600 px-4 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900"
>
Read the guide
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
<div class="mt-4 flex items-center justify-center gap-4 text-xs text-slate-500 dark:text-slate-400">
<span class="inline-flex items-center gap-1.5">
<span class="icon-[lucide--clock-3] h-4 w-4" aria-hidden="true" ></span>
12 min read
</span>
<span class="inline-flex items-center gap-1.5">
<span class="icon-[lucide--download] h-4 w-4" aria-hidden="true" ></span>
PDF available
</span>
</div>
</div>
</aside>
</div>
<!-- Bottom resource links -->
<div
class="mt-7 flex flex-wrap items-center justify-between gap-4 border-t border-slate-200 pt-5 dark:border-white/10"
>
<div class="flex flex-wrap items-center gap-x-6 gap-y-3">
<a
href="/webinars"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--presentation] h-4 w-4" aria-hidden="true" ></span>
Webinars
</a>
<a
href="/events"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--calendar-days] h-4 w-4" aria-hidden="true" ></span>
Events
</a>
<a
href="/reports"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--file-chart-column] h-4 w-4" aria-hidden="true" ></span>
Reports
</a>
<a
href="/newsletter"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--mail-plus] h-4 w-4" aria-hidden="true" ></span>
Newsletter
</a>
</div>
<a
href="/contact"
class="group inline-flex items-center gap-2 text-sm font-semibold text-indigo-600 transition hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300"
>
Contact our team
<span class="icon-[lucide--arrow-up-right] h-4 w-4 transition-transform group-hover:translate-x-0.5 group-hover:-translate-y-0.5" aria-hidden="true" ></span>
</a>
</div>
</div>
</div>
}
}
+143
View File
@@ -0,0 +1,143 @@
component SocialShare {
props {
class: string = ""
title = "Share this page"
description = ""
url = ""
shareText = ""
centered = false
compact = false
showTitle = true
showCopy = true
networks = []
}
state copied = false
view {
<aside
aria-labelledby="social-share-title"
class="w-full {class}"
>
<div
class=" relative overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-white/10 dark:bg-white/[0.03] "
>
<!-- Subtle background -->
<div
aria-hidden="true"
class="pointer-events-none absolute inset-0 overflow-hidden"
>
<div class="absolute -right-16 -top-20 h-40 w-40 rounded-full bg-indigo-200/25 blur-3xl dark:bg-indigo-500/10" ></div>
<div class="absolute inset-x-0 top-0 h-px bg-linear-to-r from-transparent via-indigo-300/60 to-transparent dark:via-indigo-500/20" ></div>
</div>
<div
class="relative"
class:p-4="compact"
class:sm:p-5="compact"
class:p-5="!compact"
class:sm:p-6="!compact"
>
<div
class="gap-4"
class:flex="centered"
class:flex-col="centered"
class:items-center="centered"
class:text-center="centered"
class:flex="!centered"
class:flex-col="!centered"
class:sm:flex-row="!centered"
class:sm:items-center="!centered"
class:sm:justify-between="!centered"
>
<!-- Heading -->
<div
class="min-w-0"
class:hidden="!showTitle"
>
<h2
id="social-share-title"
class="font-bold tracking-tight text-slate-950 dark:text-white"
class:text-sm="compact"
class:text-base="!compact"
>
{title}
</h2>
<p
class="mt-1 text-sm leading-6 text-slate-500 dark:text-slate-400"
class:hidden="description === '' || compact"
>
{description}
</p>
</div>
<!-- Share buttons -->
<div
class="flex flex-wrap items-center gap-2"
class:justify-center="centered"
class:justify-start="!centered"
>
{#each networks.slice(0, 5) as network}
<a
href="{network.href}"
target="_blank"
rel="noopener noreferrer"
aria-label="Share on {network.label}"
title="Share on {network.label}"
class=" group inline-flex items-center justify-center gap-2 rounded-xl border border-slate-200 bg-white text-sm font-semibold text-slate-600 shadow-sm transition hover:-translate-y-0.5 hover:border-indigo-300 hover:bg-indigo-50 hover:text-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-300 dark:hover:border-indigo-500/30 dark:hover:bg-indigo-500/10 dark:hover:text-indigo-300 "
class:h-9="compact"
class:w-9="compact"
class:h-10="!compact"
class:px-3="!compact"
>
<span class="{network.iconClass} h-4 w-4" aria-hidden="true" ></span>
<span class:hidden="compact">
{network.label}
</span>
</a>
{/each}
<!-- Copy link -->
<button
type="button"
@click=" navigator.clipboard.writeText(url); copied = true; "
aria-label="{copied ? 'Link copied' : 'Copy page link'}"
class=" group inline-flex items-center justify-center gap-2 rounded-xl border border-slate-200 bg-white text-sm font-semibold text-slate-600 shadow-sm transition hover:-translate-y-0.5 hover:border-indigo-300 hover:bg-indigo-50 hover:text-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-300 dark:hover:border-indigo-500/30 dark:hover:bg-indigo-500/10 dark:hover:text-indigo-300 "
class:hidden="!showCopy"
class:h-9="compact"
class:w-9="compact"
class:h-10="!compact"
class:px-3="!compact"
>
<span class="icon-[lucide--copy] h-4 w-4" class:hidden="copied" aria-hidden="true" ></span>
<span class="icon-[lucide--check] h-4 w-4" class:hidden="!copied" aria-hidden="true" ></span>
<span class:hidden="compact">
{copied ? 'Copied' : 'Copy link'}
</span>
</button>
</div>
</div>
<!-- Copy feedback -->
<div
role="status"
aria-live="polite"
class="mt-3 rounded-xl bg-emerald-50 px-3 py-2 text-sm font-medium text-emerald-700 ring-1 ring-inset ring-emerald-200 dark:bg-emerald-500/10 dark:text-emerald-300 dark:ring-emerald-500/20"
class:hidden="!copied"
class:text-center="centered"
>
Page link copied to your clipboard.
</div>
</div>
</div>
</aside>
}
}
+366 -8
View File
@@ -1,10 +1,368 @@
component SolutionsMegaMenu {
props {
title = ""
description = ""
open = false
closeLabel = "Close"
class = ""
}
view { <div data-show="open" class="wire-overlay wire-catalog-overlay {class}"><section role="dialog" aria-modal="true" aria-label="{title}" class="wire-dialog"><header><h2 data-show="title" class="wire-dialog__title">{title}</h2><p data-show="description" class="wire-dialog__description">{description}</p></header><slot /><button type="button" class="wire-btn wire-btn--ghost">{closeLabel}</button></section></div> }
props {
class: string = ""
}
view {
<div
class="border-t border-slate-200 bg-white shadow-2xl shadow-slate-950/10 dark:border-white/10 dark:bg-slate-950 dark:shadow-black/30 {class}"
>
<div class="mx-auto max-w-7xl px-6 py-8 lg:px-10">
<div class="grid gap-8 lg:grid-cols-[minmax(0,1.6fr)_minmax(18rem,0.75fr)]">
<!-- Solution links -->
<section>
<div class="mb-5 flex items-end justify-between gap-6">
<div>
<p class="text-xs font-semibold uppercase tracking-[0.16em] text-indigo-600 dark:text-indigo-400">
Solutions
</p>
<h2 class="mt-2 text-lg font-bold tracking-tight text-slate-950 dark:text-white">
Identity infrastructure for every stage of growth
</h2>
<p class="mt-1 text-sm text-slate-500 dark:text-slate-400">
Choose a solution designed around your product, team, and customers.
</p>
</div>
<a
href="/solutions"
class="group hidden items-center gap-2 text-sm font-semibold text-indigo-600 transition hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300 xl:inline-flex"
>
View all solutions
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
</div>
<div class="grid gap-2 md:grid-cols-2">
<!-- Startups -->
<a
href="/solutions/startups"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-indigo-200 hover:bg-indigo-50 dark:hover:border-indigo-500/20 dark:hover:bg-indigo-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-indigo-100 text-indigo-700 transition group-hover:bg-indigo-600 group-hover:text-white dark:bg-indigo-500/15 dark:text-indigo-300 dark:group-hover:bg-indigo-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--rocket] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Startups
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-indigo-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-indigo-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Launch secure authentication quickly without slowing product development.
</span>
</span>
</a>
<!-- SaaS -->
<a
href="/solutions/saas"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-cyan-200 hover:bg-cyan-50 dark:hover:border-cyan-500/20 dark:hover:bg-cyan-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-cyan-100 text-cyan-700 transition group-hover:bg-cyan-600 group-hover:text-white dark:bg-cyan-500/15 dark:text-cyan-300 dark:group-hover:bg-cyan-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--cloud] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
SaaS platforms
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-cyan-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-cyan-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Add users, organizations, roles, SSO, and secure account recovery.
</span>
</span>
</a>
<!-- Enterprise -->
<a
href="/solutions/enterprise"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-violet-200 hover:bg-violet-50 dark:hover:border-violet-500/20 dark:hover:bg-violet-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-violet-100 text-violet-700 transition group-hover:bg-violet-600 group-hover:text-white dark:bg-violet-500/15 dark:text-violet-300 dark:group-hover:bg-violet-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--building-2] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Enterprise
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-violet-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-violet-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Support complex organizations, governance, auditability, and scale.
</span>
</span>
</a>
<!-- Developers -->
<a
href="/solutions/developers"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-emerald-200 hover:bg-emerald-50 dark:hover:border-emerald-500/20 dark:hover:bg-emerald-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-emerald-100 text-emerald-700 transition group-hover:bg-emerald-600 group-hover:text-white dark:bg-emerald-500/15 dark:text-emerald-300 dark:group-hover:bg-emerald-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--code-2] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Developers
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-emerald-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-emerald-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
APIs, SDKs, webhooks, templates, and developer-friendly workflows.
</span>
</span>
</a>
<!-- Agencies -->
<a
href="/solutions/agencies"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-amber-200 hover:bg-amber-50 dark:hover:border-amber-500/20 dark:hover:bg-amber-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-amber-100 text-amber-700 transition group-hover:bg-amber-600 group-hover:text-white dark:bg-amber-500/15 dark:text-amber-300 dark:group-hover:bg-amber-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--briefcase-business] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Agencies
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-amber-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-amber-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Manage identities across customer projects and branded experiences.
</span>
</span>
</a>
<!-- B2B -->
<a
href="/solutions/b2b"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-rose-200 hover:bg-rose-50 dark:hover:border-rose-500/20 dark:hover:bg-rose-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-rose-100 text-rose-700 transition group-hover:bg-rose-600 group-hover:text-white dark:bg-rose-500/15 dark:text-rose-300 dark:group-hover:bg-rose-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--handshake] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
B2B products
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-rose-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-rose-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Secure customer organizations, team access, and enterprise connections.
</span>
</span>
</a>
</div>
<a
href="/solutions"
class="mt-4 flex items-center justify-center gap-2 rounded-xl border border-indigo-200 bg-indigo-50 px-4 py-3 text-sm font-semibold text-indigo-700 transition hover:border-indigo-300 hover:bg-indigo-100 dark:border-indigo-500/20 dark:bg-indigo-500/10 dark:text-indigo-300 dark:hover:border-indigo-500/30 dark:hover:bg-indigo-500/15 xl:hidden"
>
View all solutions
<span class="icon-[lucide--arrow-right] h-4 w-4" aria-hidden="true" ></span>
</a>
</section>
<!-- Featured solution -->
<aside
class="relative overflow-hidden rounded-3xl border border-violet-200 bg-linear-to-br from-violet-50 via-white to-indigo-50 p-6 dark:border-violet-500/20 dark:from-violet-500/10 dark:via-slate-900 dark:to-indigo-500/10"
>
<div aria-hidden="true" class="absolute -right-16 -top-16 h-40 w-40 rounded-full bg-violet-300/30 blur-3xl dark:bg-violet-500/10" ></div>
<div aria-hidden="true" class="absolute -bottom-20 -left-16 h-40 w-40 rounded-full bg-indigo-300/30 blur-3xl dark:bg-indigo-500/10" ></div>
<div class="relative">
<span
class="inline-flex items-center gap-2 rounded-full border border-violet-200 bg-white/80 px-3 py-1.5 text-xs font-semibold text-violet-700 backdrop-blur-sm dark:border-violet-500/20 dark:bg-white/5 dark:text-violet-300"
>
<span class="icon-[lucide--wand-sparkles] h-3.5 w-3.5" aria-hidden="true" ></span>
Recommended for SaaS
</span>
<div
class="mt-5 grid h-12 w-12 place-items-center rounded-2xl bg-violet-600 text-white shadow-lg shadow-violet-600/20"
>
<span class="icon-[lucide--cloud-cog] h-6 w-6" aria-hidden="true" ></span>
</div>
<h3 class="mt-5 text-xl font-bold tracking-tight text-slate-950 dark:text-white">
Launch enterprise-ready identity
</h3>
<p class="mt-3 text-sm leading-6 text-slate-600 dark:text-slate-400">
Start with simple authentication and add organizations,
permissions, enterprise SSO, and governance as your product grows.
</p>
<div class="mt-5 grid gap-3">
<div
class="flex items-center gap-3 rounded-xl border border-violet-200 bg-white/70 px-3 py-3 dark:border-violet-500/20 dark:bg-white/5"
>
<span
class="grid h-8 w-8 shrink-0 place-items-center rounded-lg bg-violet-100 text-violet-700 dark:bg-violet-500/15 dark:text-violet-300"
>
<span class="icon-[lucide--timer-reset] h-4 w-4" aria-hidden="true" ></span>
</span>
<span>
<span class="block text-sm font-semibold text-slate-900 dark:text-white">
Faster launch
</span>
<span class="block text-xs text-slate-500 dark:text-slate-400">
Start with secure defaults
</span>
</span>
</div>
<div
class="flex items-center gap-3 rounded-xl border border-violet-200 bg-white/70 px-3 py-3 dark:border-violet-500/20 dark:bg-white/5"
>
<span
class="grid h-8 w-8 shrink-0 place-items-center rounded-lg bg-indigo-100 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300"
>
<span class="icon-[lucide--expand] h-4 w-4" aria-hidden="true" ></span>
</span>
<span>
<span class="block text-sm font-semibold text-slate-900 dark:text-white">
Built to scale
</span>
<span class="block text-xs text-slate-500 dark:text-slate-400">
Add advanced controls later
</span>
</span>
</div>
<div
class="flex items-center gap-3 rounded-xl border border-violet-200 bg-white/70 px-3 py-3 dark:border-violet-500/20 dark:bg-white/5"
>
<span
class="grid h-8 w-8 shrink-0 place-items-center rounded-lg bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300"
>
<span class="icon-[lucide--shield-check] h-4 w-4" aria-hidden="true" ></span>
</span>
<span>
<span class="block text-sm font-semibold text-slate-900 dark:text-white">
Secure by design
</span>
<span class="block text-xs text-slate-500 dark:text-slate-400">
Protect every account flow
</span>
</span>
</div>
</div>
<a
href="/solutions/saas"
class="group mt-6 flex h-11 w-full items-center justify-center gap-2 rounded-xl bg-violet-600 px-4 text-sm font-bold text-white shadow-lg shadow-violet-600/20 transition hover:-translate-y-0.5 hover:bg-violet-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-violet-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900"
>
Explore the SaaS solution
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
</div>
</aside>
</div>
<!-- Bottom use-case links -->
<div
class="mt-7 flex flex-wrap items-center justify-between gap-4 border-t border-slate-200 pt-5 dark:border-white/10"
>
<div class="flex flex-wrap items-center gap-x-6 gap-y-3">
<a
href="/solutions/customer-identity"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--contact-round] h-4 w-4" aria-hidden="true" ></span>
Customer identity
</a>
<a
href="/solutions/workforce-identity"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--id-card] h-4 w-4" aria-hidden="true" ></span>
Workforce identity
</a>
<a
href="/solutions/marketplaces"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--store] h-4 w-4" aria-hidden="true" ></span>
Marketplaces
</a>
<a
href="/solutions/multi-tenant"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--network] h-4 w-4" aria-hidden="true" ></span>
Multi-tenant apps
</a>
</div>
<a
href="/customers"
class="group inline-flex items-center gap-2 text-sm font-semibold text-indigo-600 transition hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300"
>
See customer stories
<span class="icon-[lucide--arrow-up-right] h-4 w-4 transition-transform group-hover:translate-x-0.5 group-hover:-translate-y-0.5" aria-hidden="true" ></span>
</a>
</div>
</div>
</div>
}
}
+122
View File
@@ -0,0 +1,122 @@
component StatusBanner {
props {
class: string = ""
type = "info"
title = ""
description = ""
actionLabel = ""
actionHref = ""
dismissible = true
compact = false
details = []
}
state visible = true
view {
<aside
role="{type === 'error' ? 'alert' : 'status'}"
aria-live="{type === 'error' ? 'assertive' : 'polite'}"
class="relative overflow-hidden {class}"
class:hidden="!visible"
>
<div
class=" relative overflow-hidden rounded-2xl {compact ? 'px-4 py-3' : 'px-5 py-4 sm:px-6'} {type === 'success' ? 'bg-emerald-50 text-emerald-950 ring-1 ring-inset ring-emerald-200 dark:bg-emerald-500/10 dark:text-emerald-100 dark:ring-emerald-500/20' : type === 'warning' ? 'bg-amber-50 text-amber-950 ring-1 ring-inset ring-amber-200 dark:bg-amber-500/10 dark:text-amber-100 dark:ring-amber-500/20' : type === 'error' ? 'bg-rose-50 text-rose-950 ring-1 ring-inset ring-rose-200 dark:bg-rose-500/10 dark:text-rose-100 dark:ring-rose-500/20' : 'bg-indigo-50 text-indigo-950 ring-1 ring-inset ring-indigo-200 dark:bg-indigo-500/10 dark:text-indigo-100 dark:ring-indigo-500/20' } "
>
<!-- Decorative glow -->
<div aria-hidden="true" class=" pointer-events-none absolute -right-16 -top-20 h-40 w-40 rounded-full blur-3xl {type === 'success' ? 'bg-emerald-300/30 dark:bg-emerald-500/10' : type === 'warning' ? 'bg-amber-300/30 dark:bg-amber-500/10' : type === 'error' ? 'bg-rose-300/30 dark:bg-rose-500/10' : 'bg-indigo-300/30 dark:bg-indigo-500/10' } " ></div>
<div
class="relative flex gap-4"
class:items-center="compact"
class:items-start="!compact"
>
<!-- Icon -->
<span
class=" grid shrink-0 place-items-center rounded-xl {compact ? 'h-9 w-9' : 'h-10 w-10'} {type === 'success' ? 'bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300' : type === 'warning' ? 'bg-amber-100 text-amber-700 dark:bg-amber-500/15 dark:text-amber-300' : type === 'error' ? 'bg-rose-100 text-rose-700 dark:bg-rose-500/15 dark:text-rose-300' : 'bg-indigo-100 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300' } "
>
<span class=" h-5 w-5 {type === 'success' ? 'icon-[lucide--circle-check]' : type === 'warning' ? 'icon-[lucide--triangle-alert]' : type === 'error' ? 'icon-[lucide--circle-x]' : 'icon-[lucide--info]' } " aria-hidden="true" ></span>
</span>
<!-- Content -->
<div class="min-w-0 flex-1">
<div
class="flex flex-col gap-3 sm:flex-row sm:justify-between"
class:sm:items-center="compact"
class:sm:items-start="!compact"
>
<div class="min-w-0">
<h2
class="font-bold"
class:text-sm="compact"
class:text-base="!compact"
class:hidden="title === ''"
>
{title}
</h2>
<p
class="text-sm leading-6 opacity-80"
class:mt-1="title !== ''"
class:hidden="description === ''"
>
{description}
</p>
</div>
<!-- Actions -->
<div class="flex shrink-0 items-center gap-2">
<a
href="{actionHref}"
class=" h-9 items-center justify-center gap-1.5 rounded-lg px-3 text-xs font-bold transition {type === 'success' ? 'bg-emerald-600 text-white hover:bg-emerald-500' : type === 'warning' ? 'bg-amber-600 text-white hover:bg-amber-500' : type === 'error' ? 'bg-rose-600 text-white hover:bg-rose-500' : 'bg-indigo-600 text-white hover:bg-indigo-500' } "
class:hidden="actionLabel === ''"
class:inline-flex="actionLabel !== ''"
>
{actionLabel}
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5" aria-hidden="true" ></span>
</a>
<button
type="button"
@click="visible = false"
aria-label="Dismiss notice"
class="grid h-9 w-9 place-items-center rounded-lg opacity-60 transition hover:bg-black/5 hover:opacity-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-current dark:hover:bg-white/10"
class:hidden="!dismissible"
>
<span class="icon-[lucide--x] h-4 w-4" aria-hidden="true" ></span>
</button>
</div>
</div>
<!-- Dynamic details -->
<div
class="mt-4 flex flex-wrap gap-x-5 gap-y-2 border-t pt-4 text-xs font-medium"
class:hidden="compact || details.length === 0"
class:border-emerald-200="type === 'success'"
class:border-amber-200="type === 'warning'"
class:border-rose-200="type === 'error'"
class:border-indigo-200="type === 'info'"
class:dark:border-emerald-500/20="type === 'success'"
class:dark:border-amber-500/20="type === 'warning'"
class:dark:border-rose-500/20="type === 'error'"
class:dark:border-indigo-500/20="type === 'info'"
>
{#each details.slice(0, 4) as detail}
<span class="inline-flex items-center gap-2 opacity-80">
<span class="{detail.iconClass} h-3.5 w-3.5" aria-hidden="true" ></span>
{detail.label}
</span>
{/each}
</div>
</div>
</div>
</div>
</aside>
}
}
+212
View File
@@ -0,0 +1,212 @@
component ToastHost {
props {
class: string = ""
}
state successOpen = false
state errorOpen = false
state warningOpen = false
state infoOpen = false
state successTitle = "Changes saved"
state successMessage = "Your changes were saved successfully."
state errorTitle = "Something went wrong"
state errorMessage = "We could not complete your request. Please try again."
state warningTitle = "Action required"
state warningMessage = "Review the highlighted information before continuing."
state infoTitle = "New update available"
state infoMessage = "A newer version of this feature is now available."
view {
<div
data-toast-host
aria-live="polite"
aria-relevant="additions text"
class="pointer-events-none fixed inset-x-0 top-0 z-[120] flex flex-col items-end gap-3 p-4 sm:left-auto sm:right-0 sm:w-full sm:max-w-md sm:p-6 {class}"
>
<!-- Success toast -->
<article
role="status"
class="pointer-events-auto relative w-full overflow-hidden rounded-2xl border border-emerald-200 bg-white shadow-2xl shadow-slate-950/15 dark:border-emerald-500/20 dark:bg-slate-900 dark:shadow-black/40"
class:hidden="!successOpen"
class:block="successOpen"
>
<div class="absolute inset-y-0 left-0 w-1 bg-emerald-500"></div>
<div class="flex items-start gap-3 p-4 pl-5">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300"
>
<span class="icon-[lucide--circle-check] h-5 w-5" aria-hidden="true" ></span>
</span>
<div class="min-w-0 flex-1 pt-0.5">
<h3 class="text-sm font-bold text-slate-950 dark:text-white">
{successTitle}
</h3>
<p class="mt-1 text-sm leading-5 text-slate-600 dark:text-slate-400">
{successMessage}
</p>
</div>
<button
type="button"
@click="successOpen = false"
aria-label="Dismiss notification"
class="grid h-8 w-8 shrink-0 place-items-center rounded-lg text-slate-400 transition hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-emerald-500 dark:hover:bg-white/10 dark:hover:text-white"
>
<span class="icon-[lucide--x] h-4 w-4" aria-hidden="true" ></span>
</button>
</div>
<div class="h-1 w-full bg-emerald-100 dark:bg-emerald-500/10">
<div class="h-full w-full origin-left bg-emerald-500"></div>
</div>
</article>
<!-- Error toast -->
<article
role="alert"
class="pointer-events-auto relative w-full overflow-hidden rounded-2xl border border-rose-200 bg-white shadow-2xl shadow-slate-950/15 dark:border-rose-500/20 dark:bg-slate-900 dark:shadow-black/40"
class:hidden="!errorOpen"
class:block="errorOpen"
>
<div class="absolute inset-y-0 left-0 w-1 bg-rose-500"></div>
<div class="flex items-start gap-3 p-4 pl-5">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl bg-rose-100 text-rose-700 dark:bg-rose-500/15 dark:text-rose-300"
>
<span class="icon-[lucide--circle-x] h-5 w-5" aria-hidden="true" ></span>
</span>
<div class="min-w-0 flex-1 pt-0.5">
<h3 class="text-sm font-bold text-slate-950 dark:text-white">
{errorTitle}
</h3>
<p class="mt-1 text-sm leading-5 text-slate-600 dark:text-slate-400">
{errorMessage}
</p>
<button
type="button"
class="mt-3 inline-flex items-center gap-1.5 text-xs font-bold text-rose-700 transition hover:text-rose-900 dark:text-rose-300 dark:hover:text-rose-200"
>
Try again
<span class="icon-[lucide--rotate-cw] h-3.5 w-3.5" aria-hidden="true" ></span>
</button>
</div>
<button
type="button"
@click="errorOpen = false"
aria-label="Dismiss notification"
class="grid h-8 w-8 shrink-0 place-items-center rounded-lg text-slate-400 transition hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-rose-500 dark:hover:bg-white/10 dark:hover:text-white"
>
<span class="icon-[lucide--x] h-4 w-4" aria-hidden="true" ></span>
</button>
</div>
<div class="h-1 w-full bg-rose-100 dark:bg-rose-500/10">
<div class="h-full w-full origin-left bg-rose-500"></div>
</div>
</article>
<!-- Warning toast -->
<article
role="status"
class="pointer-events-auto relative w-full overflow-hidden rounded-2xl border border-amber-200 bg-white shadow-2xl shadow-slate-950/15 dark:border-amber-500/20 dark:bg-slate-900 dark:shadow-black/40"
class:hidden="!warningOpen"
class:block="warningOpen"
>
<div class="absolute inset-y-0 left-0 w-1 bg-amber-500"></div>
<div class="flex items-start gap-3 p-4 pl-5">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl bg-amber-100 text-amber-700 dark:bg-amber-500/15 dark:text-amber-300"
>
<span class="icon-[lucide--triangle-alert] h-5 w-5" aria-hidden="true" ></span>
</span>
<div class="min-w-0 flex-1 pt-0.5">
<h3 class="text-sm font-bold text-slate-950 dark:text-white">
{warningTitle}
</h3>
<p class="mt-1 text-sm leading-5 text-slate-600 dark:text-slate-400">
{warningMessage}
</p>
</div>
<button
type="button"
@click="warningOpen = false"
aria-label="Dismiss notification"
class="grid h-8 w-8 shrink-0 place-items-center rounded-lg text-slate-400 transition hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-amber-500 dark:hover:bg-white/10 dark:hover:text-white"
>
<span class="icon-[lucide--x] h-4 w-4" aria-hidden="true" ></span>
</button>
</div>
<div class="h-1 w-full bg-amber-100 dark:bg-amber-500/10">
<div class="h-full w-full origin-left bg-amber-500"></div>
</div>
</article>
<!-- Information toast -->
<article
role="status"
class="pointer-events-auto relative w-full overflow-hidden rounded-2xl border border-indigo-200 bg-white shadow-2xl shadow-slate-950/15 dark:border-indigo-500/20 dark:bg-slate-900 dark:shadow-black/40"
class:hidden="!infoOpen"
class:block="infoOpen"
>
<div class="absolute inset-y-0 left-0 w-1 bg-indigo-500"></div>
<div class="flex items-start gap-3 p-4 pl-5">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl bg-indigo-100 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300"
>
<span class="icon-[lucide--info] h-5 w-5" aria-hidden="true" ></span>
</span>
<div class="min-w-0 flex-1 pt-0.5">
<h3 class="text-sm font-bold text-slate-950 dark:text-white">
{infoTitle}
</h3>
<p class="mt-1 text-sm leading-5 text-slate-600 dark:text-slate-400">
{infoMessage}
</p>
<a
href="/changelog"
class="mt-3 inline-flex items-center gap-1.5 text-xs font-bold text-indigo-700 transition hover:text-indigo-900 dark:text-indigo-300 dark:hover:text-indigo-200"
>
View update
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5" aria-hidden="true" ></span>
</a>
</div>
<button
type="button"
@click="infoOpen = false"
aria-label="Dismiss notification"
class="grid h-8 w-8 shrink-0 place-items-center rounded-lg text-slate-400 transition hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:hover:bg-white/10 dark:hover:text-white"
>
<span class="icon-[lucide--x] h-4 w-4" aria-hidden="true" ></span>
</button>
</div>
<div class="h-1 w-full bg-indigo-100 dark:bg-indigo-500/10">
<div class="h-full w-full origin-left bg-indigo-500"></div>
</div>
</article>
</div>
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ui",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/ui — part of the WrNexus framework.",
"license": "MIT",
@@ -25,7 +25,7 @@
"./ui.css": "./ui.css"
},
"dependencies": {
"@wrnexus/core": "^0.2.61"
"@wrnexus/core": "^0.2.62"
},
"files": [
"dist",
+45 -3
View File
@@ -1050,9 +1050,10 @@
--wire-ease-emphasized: cubic-bezier(0.16, 1, 0.3, 1);
}
/* Tailwind v4 color bridge. Existing utility-authored components inherit the
* configured semantic palette without requiring generated per-palette markup. */
[data-component] {
/* Tailwind v4 color bridge. Define the aliases at the document root so both
* packaged components and application-authored pages/components inherit the
* palette selected in wrnexus.config.ts. */
:root {
--color-violet-50: color-mix(in srgb, var(--wire-color-primary) 7%, white);
--color-violet-100: color-mix(in srgb, var(--wire-color-primary) 14%, white);
--color-violet-200: color-mix(in srgb, var(--wire-color-primary) 25%, white);
@@ -1096,6 +1097,47 @@
--color-rose-700: color-mix(in srgb, var(--wire-color-danger) 80%, black);
}
/* Stable semantic helpers for application-authored markup. These deliberately
* avoid spacing so applications remain in control of their own layout. */
.wire-bg-page {
background-color: var(--wire-color-bg);
}
.wire-bg-surface {
background-color: var(--wire-color-surface);
}
.wire-bg-surface-2 {
background-color: var(--wire-color-surface-2);
}
.wire-bg-primary {
background-color: var(--wire-color-primary);
color: var(--wire-color-primary-contrast);
}
.wire-bg-secondary {
background-color: var(--wire-color-secondary);
color: var(--wire-color-secondary-contrast);
}
.wire-text {
color: var(--wire-color-text);
}
.wire-text-muted {
color: var(--wire-color-muted);
}
.wire-text-primary {
color: var(--wire-color-primary);
}
.wire-text-success {
color: var(--wire-color-success);
}
.wire-text-warning {
color: var(--wire-color-warning);
}
.wire-text-danger {
color: var(--wire-color-danger);
}
.wire-border {
border-color: var(--wire-color-border);
}
[data-component] {
animation: wire-component-enter var(--wire-motion-slow) var(--wire-ease-emphasized) both;
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/uploader",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/uploader — part of the WrNexus framework.",
"license": "MIT",
@@ -21,7 +21,7 @@
}
},
"dependencies": {
"@wrnexus/core": "^0.2.61"
"@wrnexus/core": "^0.2.62"
},
"files": [
"dist"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/validation",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"description": "@wrnexus/validation — part of the WrNexus framework.",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ai",
"version": "0.2.61",
"version": "0.2.62",
"private": true,
"type": "module",
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/authz",
"version": "0.2.61",
"version": "0.2.62",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/cli",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"main": "src/index.ts",
"exports": {
+9
View File
@@ -619,6 +619,15 @@ const MIGRATIONS: Migration[] = [
// The UI component and stylesheet updates apply automatically.
},
},
{
version: "0.2.62",
id: "application-theme-contract-and-public-ui",
description:
"Exposes semantic theme helpers to application UI and promotes the public-site components into @wrnexus/ui.",
apply() {
// Package auto-discovery and the root-level theme aliases apply automatically.
},
},
];
/** Release tooling uses this to require an explicit migration entry per version. */
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/compiler",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/core",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/csr",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/db",
"version": "0.2.61",
"version": "0.2.62",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/dev-server",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/encryption",
"version": "0.2.61",
"version": "0.2.62",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/helpers",
"version": "0.2.61",
"version": "0.2.62",
"private": true,
"type": "module",
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/i18n",
"version": "0.2.61",
"version": "0.2.62",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/jwt",
"version": "0.2.61",
"version": "0.2.62",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/mobile",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/native",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/oauth",
"version": "0.2.61",
"version": "0.2.62",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/pubsub",
"version": "0.2.61",
"version": "0.2.62",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/queue",
"version": "0.2.61",
"version": "0.2.62",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/reactive",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/router",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ssr",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/styles",
"version": "0.2.61",
"version": "0.2.62",
"type": "module",
"main": "src/index.ts",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/test",
"version": "0.2.61",
"version": "0.2.62",
"private": true,
"type": "module",
"main": "src/index.ts",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/tracking",
"version": "0.2.61",
"version": "0.2.62",
"private": true,
"type": "module",
"main": "src/index.ts",
+110 -20
View File
@@ -1,6 +1,6 @@
# WRNexus UI component reference
This reference is generated from the 891 packaged `.wrn` component sources. Props marked required have no default; all others show their runtime default.
This reference is generated from the 901 packaged `.wrn` component sources. Props marked required have no default; all others show their runtime default.
## Actions
@@ -1964,9 +1964,9 @@ Reusable navigation item component.
Reusable newsletter form component.
- Mount: `data-component="NewsletterForm"`
- Props: `eyebrow: string = ""`, `title: string = ""`, `description: string = ""`, `href: string = ""`, `actionLabel: string = "Learn more"`, `image: string = ""`, `alt: string = ""`, `class: string = ""`
- Slots: `default`
- Events: None
- Props: `class: string = ""`, `eyebrow: string = ""`, `title: string = "Stay up to date"`, `description: string = ""`, `placeholder: string = "Enter your email"`, `buttonLabel: string = "Subscribe"`, `action: string = "/newsletter/subscribe"`, `method: string = "post"`, `emailName: string = "email"`, `privacyLabel: string = ""`, `privacyHref: string = "/privacy"`, `successMessage: string = "You are subscribed."`, `errorMessage: string = "Something went wrong. Please try again."`, `iconClass: string = "icon-[lucide--mail]"`, `centered: boolean = false`, `compact: boolean = false`, `showIcon: boolean = true`, `showPrivacy: boolean = true`, `fullWidth: boolean = false`
- Slots: None
- Events: `submit`, `input`
### NotFoundPage
@@ -2911,7 +2911,7 @@ Reusable back to top component.
Reusable breadcrumbs component.
- Mount: `data-component="Breadcrumbs"`
- Props: `class: string = ""`, `homeLabel: string = "Home"`, `homeHref: string = "/"`, `current: string = "Current page"`
- Props: `class: string = ""`, `items: string = []`, `centered: boolean = false`, `compact: boolean = false`, `showHome: boolean = true`, `homeLabel: string = "Home"`, `homeHref: string = "/"`
- Slots: None
- Events: None
@@ -2951,6 +2951,15 @@ Reusable combobox component.
- Slots: None
- Events: None
### ContactSalesBanner
Reusable contact sales banner component.
- Mount: `data-component="ContactSalesBanner"`
- Props: `class: string = ""`, `eyebrow: string = "Enterprise identity"`, `title: string = "Ready to secure your next application?"`, `description: string = ""`, `primaryLabel: string = "Contact sales"`, `primaryHref: string = "/contact"`, `secondaryLabel: string = "Start free"`, `secondaryHref: string = "/sign-up"`, `iconClass: string = "icon-[lucide--messages-square]"`, `centered: boolean = false`, `compact: boolean = false`, `points: string = []`, `trustPoints: string = []`
- Slots: None
- Events: None
### Container
Reusable container component.
@@ -2960,6 +2969,24 @@ Reusable container component.
- Slots: `default`
- Events: None
### CookieConsent
Reusable cookie consent component.
- Mount: `data-component="CookieConsent"`
- Props: `class: string = ""`
- Slots: None
- Events: `click`, `change`
### DevelopersMegaMenu
Reusable developers mega menu component.
- Mount: `data-component="DevelopersMegaMenu"`
- Props: `class: string = ""`
- Slots: None
- Events: None
### disclosure
Reusable disclosure component.
@@ -3109,8 +3136,8 @@ Reusable link component.
Reusable logo cloud component.
- Mount: `data-component="LogoCloud"`
- Props: `class: string = ""`, `title: string = "Trusted by modern teams"`
- Slots: `default`
- Props: `class: string = ""`, `eyebrow: string = ""`, `title: string = "Trusted by teams building modern products"`, `description: string = ""`, `logos: string = []`, `centered: boolean = true`, `compact: boolean = false`, `variant: string = "strip"`, `grayscale: boolean = true`, `showNames: boolean = false`, `maxItems: number = 8`
- Slots: None
- Events: None
### Modal
@@ -3149,6 +3176,15 @@ Reusable notification dot component.
- Slots: `default`
- Events: None
### PageHeader
Reusable page header component.
- Mount: `data-component="PageHeader"`
- Props: `class: string = ""`, `eyebrow: string = ""`, `title: string = ""`, `description: string = ""`, `primaryLabel: string = ""`, `primaryHref: string = ""`, `secondaryLabel: string = ""`, `secondaryHref: string = ""`, `icon: string = "sparkles"`, `centered: boolean = false`, `compact: boolean = false`, `showBreadcrumbs: boolean = false`, `breadcrumbParent: string = ""`, `breadcrumbParentHref: string = ""`, `breadcrumbCurrent: string = ""`, `highlights: string = []`
- Slots: None
- Events: None
### PageHeading
Reusable page heading component.
@@ -3172,7 +3208,7 @@ Reusable page shell component.
Reusable pagination component.
- Mount: `data-component="Pagination"`
- Props: `class: string = ""`, `page: number = 1`, `totalPages: number = 1`, `previousHref: string = "#"`, `nextHref: string = "#"`
- Props: `class: string = ""`, `currentPage: number = 1`, `totalPages: number = 1`, `previousHref: string = ""`, `nextHref: string = ""`, `pages: string = []`, `showNumbers: boolean = true`, `showSummary: boolean = true`, `totalItems: number = 0`, `pageSize: number = 10`, `compact: boolean = false`, `centered: boolean = false`
- Slots: None
- Events: None
@@ -3185,6 +3221,15 @@ Reusable pill component.
- Slots: `default`
- Events: None
### ProductMegaMenu
Reusable product mega menu component.
- Mount: `data-component="ProductMegaMenu"`
- Props: `class: string = ""`
- Slots: None
- Events: None
### progress
Reusable progress component.
@@ -3208,9 +3253,27 @@ Reusable progress bar component.
Reusable public footer component.
- Mount: `data-component="PublicFooter"`
- Props: `class: string = ""`, `brand: string = "WRNexusJS"`, `description: string = "SSR-first, Bun-native full-stack framework."`, `copyright: string = "© 2026 WorkRoot Workspace"`
- Slots: `default`
- Events: None
- Props: `class: string = ""`
- Slots: None
- Events: `submit`
### PublicMobileNavigation
Reusable public mobile navigation component.
- Mount: `data-component="PublicMobileNavigation"`
- Props: `class: string = ""`
- Slots: None
- Events: `click`
### PublicSearch
Reusable public search component.
- Mount: `data-component="PublicSearch"`
- Props: `class: string = ""`, `query: string = ""`, `placeholder: string = "Search..."`, `label: string = "Search"`, `action: string = ""`, `method: string = "get"`, `name: string = "q"`, `buttonLabel: string = ""`, `clearLabel: string = "Clear search"`, `size: string = "default"`, `centered: boolean = false`, `fullWidth: boolean = false`, `showShortcut: boolean = false`, `shortcutLabel: string = "⌘ K"`, `suggestions: string = []`
- Slots: None
- Events: `input`, `focus`, `blur`, `click`
### Radio
@@ -3257,6 +3320,15 @@ Reusable select component.
- Slots: `default`
- Events: None
### SocialShare
Reusable social share component.
- Mount: `data-component="SocialShare"`
- Props: `class: string = ""`, `title: string = "Share this page"`, `description: string = ""`, `url: string = ""`, `shareText: string = ""`, `centered: boolean = false`, `compact: boolean = false`, `showTitle: boolean = true`, `showCopy: boolean = true`, `networks: string = []`
- Slots: None
- Events: `click`
### spacer
Reusable spacer component.
@@ -3284,6 +3356,15 @@ Reusable stack component.
- Slots: `default`
- Events: None
### StatusBanner
Reusable status banner component.
- Mount: `data-component="StatusBanner"`
- Props: `class: string = ""`, `type: string = "info"`, `title: string = ""`, `description: string = ""`, `actionLabel: string = ""`, `actionHref: string = ""`, `dismissible: boolean = true`, `compact: boolean = false`, `details: string = []`
- Slots: None
- Events: `click`
### Sticky
Reusable sticky component.
@@ -3356,6 +3437,15 @@ Reusable toast component.
- Slots: None
- Events: `click`
### ToastHost
Reusable toast host component.
- Mount: `data-component="ToastHost"`
- Props: `class: string = ""`
- Slots: None
- Events: `click`
### Typography
Reusable typography component.
@@ -4292,7 +4382,7 @@ Reusable deprecated feature alert component.
Reusable empty state component.
- Mount: `data-component="EmptyState"`
- Props: `class: string = ""`, `icon: string = ""`, `title: string = "Nothing here yet"`, `description: string = ""`, `actionLabel: string = ""`, `actionHref: string = "#"`
- Props: `class: string = ""`, `eyebrow: string = ""`, `title: string = "Nothing found"`, `description: string = ""`, `iconClass: string = "icon-[lucide--inbox]"`, `primaryLabel: string = ""`, `primaryHref: string = ""`, `secondaryLabel: string = ""`, `secondaryHref: string = ""`, `compact: boolean = false`, `centered: boolean = true`, `suggestions: string = []`
- Slots: None
- Events: None
@@ -4301,9 +4391,9 @@ Reusable empty state component.
Reusable error state component.
- Mount: `data-component="ErrorState"`
- Props: `class: string = ""`, `code: string = "500"`, `title: string = "Something went wrong"`, `description: string = "Please try again."`, `actionLabel: string = "Try again"`, `actionHref: string = "/"`
- Props: `class: string = ""`, `type: string = "error"`, `eyebrow: string = ""`, `title: string = "Something went wrong"`, `description: string = ""`, `errorCode: string = ""`, `iconClass: string = ""`, `primaryLabel: string = ""`, `primaryHref: string = ""`, `secondaryLabel: string = ""`, `secondaryHref: string = ""`, `retryLabel: string = ""`, `retryAction: string = ""`, `centered: boolean = true`, `compact: boolean = false`, `details: string = []`
- Slots: None
- Events: None
- Events: `click`
### FeatureUnavailableState
@@ -7113,8 +7203,8 @@ Reusable product page shell component.
Reusable public header component.
- Mount: `data-component="PublicHeader"`
- Props: `class: string = ""`, `brand: string = "WRNexusJS"`, `homeHref: string = "/"`, `signInHref: string = "/signin"`, `ctaHref: string = "/start"`, `ctaLabel: string = "Get started"`
- Slots: `default`, `mobile`
- Props: `class: string = ""`
- Slots: None
- Events: `click`
### PublicPageShell
@@ -7547,8 +7637,8 @@ Reusable prompt dialog component.
Reusable resources mega menu component.
- Mount: `data-component="ResourcesMegaMenu"`
- Props: `title: string = ""`, `description: string = ""`, `open: boolean = false`, `closeLabel: string = "Close"`, `class: string = ""`
- Slots: `default`
- Props: `class: string = ""`
- Slots: None
- Events: None
### SearchDialog
@@ -7583,8 +7673,8 @@ Reusable session expired dialog component.
Reusable solutions mega menu component.
- Mount: `data-component="SolutionsMegaMenu"`
- Props: `title: string = ""`, `description: string = ""`, `open: boolean = false`, `closeLabel: string = "Close"`, `class: string = ""`
- Slots: `default`
- Props: `class: string = ""`
- Slots: None
- Events: None
### StepUpAuthenticationDialog
+30
View File
@@ -133,6 +133,36 @@ entry and interaction motion. Override `--wire-motion-fast`,
`--wire-ease-emphasized` to tune it. Hover lift is limited to precise pointing
devices and `prefers-reduced-motion` is honored automatically.
### Using the selected theme in application UI
The active theme and palette are not limited to `@wrnexus/ui` components. The
framework exposes the resolved values as semantic CSS custom properties, so
pages and custom `.wrn` components can use the same contract:
```css
.account-card {
background: var(--wire-color-surface);
color: var(--wire-color-text);
border: 1px solid var(--wire-color-border);
}
.account-card__action {
background: var(--wire-color-primary);
color: var(--wire-color-primary-contrast);
}
```
Stable no-spacing helper classes are also available: `wire-bg-page`,
`wire-bg-surface`, `wire-bg-surface-2`, `wire-bg-primary`, `wire-bg-secondary`,
`wire-text`, `wire-text-muted`, `wire-text-primary`, `wire-text-success`,
`wire-text-warning`, `wire-text-danger`, and `wire-border`.
Tailwind-authored custom markup can continue using the palette families already
used by packaged components. `indigo-*` and `violet-*` resolve to primary,
`blue-*` to info, `emerald-*`/`green-*` to success, `amber-*` to warning, and
`red-*`/`rose-*` to danger. These aliases live at `:root`, so they work outside
a `[data-component]` boundary too.
## Usage
### Auto-discovery
File diff suppressed because it is too large Load Diff
+103 -9
View File
@@ -1,11 +1,105 @@
component Breadcrumbs {
props {
class = ""
homeLabel = "Home"
homeHref = "/"
current = "Current page"
}
view {
<nav class="{class}" aria-label="Breadcrumb"><ol class="flex flex-wrap items-center gap-2 text-sm"><li><a href="{homeHref}" class="text-slate-500 hover:text-violet-600">{homeLabel}</a></li><li aria-hidden="true" class="text-slate-400">/</li><li aria-current="page" class="font-medium text-slate-900 dark:text-white">{current}</li></ol></nav>
}
props {
class: string = ""
items = []
centered = false
compact = false
showHome = true
homeLabel = "Home"
homeHref = "/"
}
view {
<nav
aria-label="Breadcrumb"
class="w-full {class}"
class:hidden="items.length === 0 && !showHome"
>
<ol
class="flex flex-wrap items-center text-slate-500 dark:text-slate-400"
class:justify-center="centered"
class:gap-1.5="compact"
class:text-xs="compact"
class:gap-2="!compact"
class:text-sm="!compact"
>
<!-- Home -->
<li
class:hidden="!showHome"
class:inline-flex="showHome"
>
<a
href="{homeHref}"
class="group inline-flex items-center gap-1.5 rounded-lg transition hover:text-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--home] h-3.5 w-3.5" aria-hidden="true" ></span>
<span class:hidden="compact">
{homeLabel}
</span>
<span
class="sr-only"
class:hidden="!compact"
>
{homeLabel}
</span>
</a>
</li>
<!-- Separator after home -->
<li
aria-hidden="true"
class="text-slate-300 dark:text-slate-700"
class:hidden="!showHome || items.length === 0"
class:inline-flex="showHome && items.length > 0"
>
<span class="icon-[lucide--chevron-right] h-3.5 w-3.5" ></span>
</li>
<!-- Dynamic breadcrumb items -->
{#each items as item, index}
<li class="inline-flex min-w-0 items-center gap-2">
<!-- Linked item -->
<a
href="{item.href}"
class="max-w-48 truncate rounded-lg transition hover:text-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:hover:text-indigo-300 sm:max-w-64"
class:hidden="item.current || item.href === ''"
class:inline-flex="!item.current && item.href !== ''"
>
{item.label}
</a>
<!-- Current item -->
<span
aria-current="page"
class="max-w-48 truncate font-semibold text-slate-950 dark:text-white sm:max-w-64"
class:hidden="!item.current"
class:inline-flex="item.current"
>
{item.label}
</span>
<!-- Plain unlinked item -->
<span
class="max-w-48 truncate text-slate-600 dark:text-slate-300 sm:max-w-64"
class:hidden="item.current || item.href !== ''"
class:inline-flex="!item.current && item.href === ''"
>
{item.label}
</span>
<!-- Separator -->
<span
aria-hidden="true"
class="inline-flex text-slate-300 dark:text-slate-700"
class:hidden="index === items.length - 1"
>
<span class="icon-[lucide--chevron-right] h-3.5 w-3.5" ></span>
</span>
</li>
{/each}
</ol>
</nav>
}
}
@@ -0,0 +1,195 @@
component ContactSalesBanner {
props {
class: string = ""
eyebrow = "Enterprise identity"
title = "Ready to secure your next application?"
description = ""
primaryLabel = "Contact sales"
primaryHref = "/contact"
secondaryLabel = "Start free"
secondaryHref = "/sign-up"
iconClass = "icon-[lucide--messages-square]"
centered = false
compact = false
points = []
trustPoints = []
}
view {
<section
aria-labelledby="contact-sales-banner-title"
class="px-5 sm:px-8 lg:px-10 xl:px-12 {class}"
class:py-10="compact"
class:sm:py-12="compact"
class:py-14="!compact"
class:sm:py-16="!compact"
class:lg:py-20="!compact"
>
<div
class="relative mx-auto w-full max-w-7xl overflow-hidden rounded-3xl border border-indigo-200/80 bg-linear-to-br from-indigo-50 via-white to-violet-50 shadow-xl shadow-indigo-950/5 dark:border-indigo-500/20 dark:from-indigo-500/10 dark:via-slate-900 dark:to-violet-500/10 dark:shadow-black/20"
>
<!-- Background decoration -->
<div
aria-hidden="true"
class="pointer-events-none absolute inset-0 overflow-hidden"
>
<div class="absolute -right-24 -top-24 h-64 w-64 rounded-full bg-indigo-300/20 blur-3xl dark:bg-indigo-500/10"></div>
<div class="absolute -bottom-28 -left-20 h-64 w-64 rounded-full bg-violet-300/20 blur-3xl dark:bg-violet-500/10"></div>
<div class="absolute inset-x-0 top-0 h-px bg-linear-to-r from-transparent via-indigo-400/50 to-transparent dark:via-indigo-400/20"></div>
</div>
<div
class="relative"
class:px-6="compact"
class:py-7="compact"
class:sm:px-8="compact"
class:px-6="!compact"
class:py-8="!compact"
class:sm:px-9="!compact"
class:sm:py-10="!compact"
class:lg:px-12="!compact"
class:lg:py-12="!compact"
>
<!-- Main content -->
<div
class="gap-8"
class:flex="centered"
class:flex-col="centered"
class:items-center="centered"
class:text-center="centered"
class:mx-auto="centered"
class:max-w-4xl="centered"
class:grid="!centered"
class:lg:grid-cols-[minmax(0,1fr)_auto]="!centered"
class:lg:items-center="!centered"
class:lg:gap-12="!centered"
>
<!-- Copy -->
<div
class="min-w-0"
class:max-w-3xl="centered"
class:max-w-4xl="!centered"
>
<!-- Eyebrow -->
<div
class="flex"
class:hidden="eyebrow === ''"
class:justify-center="centered"
class:justify-start="!centered"
>
<span
class="inline-flex items-center gap-2 rounded-full border border-indigo-200 bg-white/80 px-3 py-1.5 text-xs font-semibold text-indigo-700 shadow-sm backdrop-blur-sm dark:border-indigo-500/20 dark:bg-white/5 dark:text-indigo-300"
>
<span class="{iconClass} h-3.5 w-3.5" aria-hidden="true" ></span>
{eyebrow}
</span>
</div>
<!-- Title -->
<h2
id="contact-sales-banner-title"
class="font-bold tracking-tight text-slate-950 dark:text-white"
class:mt-5="eyebrow !== ''"
class:text-2xl="compact"
class:leading-tight="compact"
class:sm:text-3xl="compact"
class:text-3xl="!compact"
class:leading-tight="!compact"
class:sm:text-4xl="!compact"
>
{title}
</h2>
<!-- Description -->
<p
class="mt-4 text-sm leading-7 text-slate-600 dark:text-slate-400 sm:text-base"
class:hidden="description === ''"
class:mx-auto="centered"
class:max-w-2xl="centered"
class:max-w-3xl="!centered"
>
{description}
</p>
<!-- Dynamic points -->
<div
class="mt-6 flex flex-wrap gap-x-5 gap-y-3"
class:hidden="points.length === 0"
class:justify-center="centered"
class:justify-start="!centered"
>
{#each points.slice(0, 4) as point}
<div class="inline-flex items-center gap-2 text-sm font-medium text-slate-700 dark:text-slate-300">
<span
class="grid h-6 w-6 shrink-0 place-items-center rounded-full bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300"
>
<span class="icon-[lucide--check] h-3.5 w-3.5" aria-hidden="true" ></span>
</span>
{point}
</div>
{/each}
</div>
</div>
<!-- Actions -->
<div
class="flex shrink-0 flex-col gap-3 sm:flex-row"
class:hidden="primaryLabel === '' && secondaryLabel === ''"
class:mt-1="centered"
class:justify-center="centered"
>
<a
href="{secondaryHref}"
class="h-12 items-center justify-center rounded-xl border border-slate-200 bg-white/90 px-5 text-sm font-semibold text-slate-700 shadow-sm backdrop-blur-sm transition hover:-translate-y-0.5 hover:border-indigo-300 hover:bg-white hover:text-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-200 dark:hover:border-indigo-500/30 dark:hover:bg-white/10 dark:hover:text-indigo-300"
class:hidden="secondaryLabel === ''"
class:inline-flex="secondaryLabel !== ''"
>
{secondaryLabel}
</a>
<a
href="{primaryHref}"
class="group h-12 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-5 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900"
class:hidden="primaryLabel === ''"
class:inline-flex="primaryLabel !== ''"
>
{primaryLabel}
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
</div>
</div>
<!-- Dynamic trust strip -->
<div
class="mt-8 border-t border-indigo-200/70 pt-5 dark:border-indigo-500/15"
class:hidden="compact || trustPoints.length === 0"
>
<div
class="flex flex-wrap items-center gap-x-6 gap-y-3"
class:justify-center="centered"
class:justify-start="!centered"
>
{#each trustPoints.slice(0, 4) as trustPoint}
<span class="inline-flex items-center gap-2 text-xs font-medium text-slate-500 dark:text-slate-400">
<span class="{trustPoint.iconClass} h-4 w-4 {trustPoint.iconColorClass}" aria-hidden="true" ></span>
{trustPoint.label}
</span>
{/each}
</div>
</div>
</div>
</div>
</section>
}
}
+372
View File
@@ -0,0 +1,372 @@
component CookieConsent {
props {
class: string = ""
}
state bannerOpen = false
state preferencesOpen = false
state analyticsCookies = false
state personalizationCookies = false
state marketingCookies = false
view {
<div class="{class}" class:hidden="!bannerOpen">
<!-- Cookie consent banner -->
<section
role="dialog"
aria-modal="false"
aria-labelledby="cookie-consent-title"
aria-describedby="cookie-consent-description"
class="fixed inset-x-0 bottom-0 z-[80] p-4 sm:p-5 lg:p-6 {class}"
class:hidden="!bannerOpen || preferencesOpen"
class:block="bannerOpen && !preferencesOpen"
>
<div
class="mx-auto w-full max-w-7xl overflow-hidden rounded-3xl border border-slate-200 bg-white/95 shadow-2xl shadow-slate-950/20 backdrop-blur-xl dark:border-white/10 dark:bg-slate-900/95 dark:shadow-black/40"
>
<div class="grid items-center gap-6 p-5 sm:p-6 lg:grid-cols-[minmax(0,1fr)_auto] lg:p-7">
<!-- Message -->
<div class="flex items-start gap-4">
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-2xl bg-indigo-100 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300"
>
<span class="icon-[lucide--cookie] h-5 w-5" aria-hidden="true" ></span>
</span>
<div class="min-w-0">
<div class="flex flex-wrap items-center gap-2">
<h2
id="cookie-consent-title"
class="text-base font-bold text-slate-950 dark:text-white"
>
Your privacy matters
</h2>
<span
class="inline-flex items-center rounded-full bg-emerald-100 px-2.5 py-1 text-[10px] font-bold uppercase tracking-wide text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300"
>
Privacy first
</span>
</div>
<p
id="cookie-consent-description"
class="mt-2 max-w-3xl text-sm leading-6 text-slate-600 dark:text-slate-400"
>
We use essential cookies to operate WrNexus. With your
permission, we also use analytics and personalization cookies
to improve your experience.
</p>
<div class="mt-3 flex flex-wrap items-center gap-x-4 gap-y-2">
<a
href="/cookies"
class="inline-flex items-center gap-1.5 text-xs font-semibold text-indigo-600 transition hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300"
>
Cookie policy
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5" aria-hidden="true" ></span>
</a>
<a
href="/privacy"
class="inline-flex items-center gap-1.5 text-xs font-semibold text-slate-500 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
Privacy policy
</a>
</div>
</div>
</div>
<!-- Actions -->
<div class="grid gap-2 sm:grid-cols-3 lg:flex lg:items-center">
<button
type="button"
@click=" analyticsCookies = false; personalizationCookies = false; marketingCookies = false; bannerOpen = false "
data-cookie-action="essential"
class="inline-flex h-11 items-center justify-center rounded-xl border border-slate-200 bg-white px-4 text-sm font-semibold text-slate-700 transition hover:border-slate-300 hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-200 dark:hover:border-white/20 dark:hover:bg-white/10 dark:hover:text-white"
>
Essential only
</button>
<button
type="button"
@click="preferencesOpen = true"
class="inline-flex h-11 items-center justify-center gap-2 rounded-xl border border-indigo-200 bg-indigo-50 px-4 text-sm font-semibold text-indigo-700 transition hover:border-indigo-300 hover:bg-indigo-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-indigo-500/20 dark:bg-indigo-500/10 dark:text-indigo-300 dark:hover:border-indigo-500/30 dark:hover:bg-indigo-500/15"
>
<span class="icon-[lucide--settings-2] h-4 w-4" aria-hidden="true" ></span>
Preferences
</button>
<button
type="button"
@click=" analyticsCookies = true; personalizationCookies = true; marketingCookies = true; bannerOpen = false "
data-cookie-action="accept-all"
class="inline-flex h-11 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-5 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900"
>
Accept all
<span class="icon-[lucide--check] h-4 w-4" aria-hidden="true" ></span>
</button>
</div>
</div>
</div>
</section>
<!-- Preferences backdrop -->
<button type="button" @click="preferencesOpen = false" aria-label="Close cookie preferences" class="fixed inset-0 z-[90] bg-slate-950/50 backdrop-blur-sm" class:hidden="!preferencesOpen" class:block="preferencesOpen" ></button>
<!-- Preferences modal -->
<section
role="dialog"
aria-modal="true"
aria-labelledby="cookie-preferences-title"
aria-describedby="cookie-preferences-description"
class="fixed inset-0 z-[100] overflow-y-auto p-4 sm:p-6"
class:hidden="!preferencesOpen"
class:block="preferencesOpen"
>
<div class="flex min-h-full items-end justify-center sm:items-center">
<div
class="relative w-full max-w-2xl overflow-hidden rounded-t-3xl border border-slate-200 bg-white shadow-2xl shadow-slate-950/30 dark:border-white/10 dark:bg-slate-900 dark:shadow-black/50 sm:rounded-3xl"
>
<!-- Modal header -->
<header class="border-b border-slate-200 px-5 py-5 dark:border-white/10 sm:px-6">
<div class="flex items-start justify-between gap-4">
<div class="flex items-start gap-4">
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-2xl bg-indigo-100 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300"
>
<span class="icon-[lucide--sliders-horizontal] h-5 w-5" aria-hidden="true" ></span>
</span>
<div>
<h2
id="cookie-preferences-title"
class="text-lg font-bold tracking-tight text-slate-950 dark:text-white"
>
Cookie preferences
</h2>
<p
id="cookie-preferences-description"
class="mt-1 text-sm leading-6 text-slate-500 dark:text-slate-400"
>
Choose which optional cookies WrNexus may use.
</p>
</div>
</div>
<button
type="button"
@click="preferencesOpen = false"
aria-label="Close cookie preferences"
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl border border-slate-200 bg-white text-slate-500 transition hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-400 dark:hover:bg-white/10 dark:hover:text-white"
>
<span class="icon-[lucide--x] h-4.5 w-4.5" aria-hidden="true" ></span>
</button>
</div>
</header>
<!-- Preference groups -->
<div class="max-h-[60dvh] space-y-3 overflow-y-auto p-5 sm:p-6">
<!-- Essential -->
<article
class="rounded-2xl bg-emerald-500/10 p-4 ring-1 ring-inset ring-emerald-500/15 transition dark:bg-emerald-500/10 dark:ring-emerald-400/10"
>
<div class="flex items-start justify-between gap-4">
<div class="flex items-start gap-3">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl bg-emerald-500/15 text-emerald-700 dark:bg-emerald-400/10 dark:text-emerald-300"
>
<span class="icon-[lucide--shield-check] h-4.5 w-4.5" aria-hidden="true" ></span>
</span>
<div>
<div class="flex flex-wrap items-center gap-2">
<h3 class="text-sm font-bold text-slate-950 dark:text-white">
Essential cookies
</h3>
<span
class="rounded-full bg-emerald-500/15 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-emerald-700 dark:bg-emerald-400/10 dark:text-emerald-300"
>
Always active
</span>
</div>
<p class="mt-1.5 text-xs leading-5 text-slate-600 dark:text-slate-400">
Required for authentication, security, sessions, preferences, and core website functionality.
</p>
</div>
</div>
<span
class="relative inline-flex h-6 w-11 shrink-0 cursor-not-allowed rounded-full bg-emerald-500"
aria-hidden="true"
>
<span class="absolute right-1 top-1 h-4 w-4 rounded-full bg-white shadow-sm" ></span>
</span>
</div>
</article>
<!-- Analytics -->
<article
class="rounded-2xl p-4 transition-all duration-200 {analyticsCookies ? 'bg-indigo-50 ring-1 ring-inset ring-indigo-200 dark:bg-indigo-500/10 dark:ring-indigo-500/20' : 'bg-slate-50 ring-1 ring-inset ring-slate-200 dark:bg-white/5 dark:ring-white/10' }"
>
<label class="flex cursor-pointer items-start justify-between gap-4">
<span class="flex min-w-0 items-start gap-3">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl transition-all duration-200 {analyticsCookies ? 'bg-indigo-100 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300' : 'bg-slate-200 text-slate-500 dark:bg-white/10 dark:text-slate-400' }"
>
<span class="icon-[lucide--chart-no-axes-combined] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="block text-sm font-bold text-slate-950 dark:text-white">
Analytics cookies
</span>
<span class="mt-1 block text-xs leading-5 text-slate-600 dark:text-slate-400">
Help us understand website usage, performance, and navigation patterns.
</span>
</span>
</span>
<span class="relative mt-0.5 shrink-0">
<input
type="checkbox"
name="analytics"
checked="{analyticsCookies}"
@change="analyticsCookies = event.target.checked"
class="peer sr-only"
/>
<span class="block h-6 w-11 rounded-full bg-slate-300 transition-colors duration-200 peer-checked:bg-indigo-600 peer-focus-visible:ring-4 peer-focus-visible:ring-indigo-500/20 dark:bg-slate-700" ></span>
<span class="absolute left-1 top-1 h-4 w-4 rounded-full bg-white shadow-sm transition-transform duration-200 peer-checked:translate-x-5" ></span>
</span>
</label>
</article>
<!-- Personalization -->
<article
class="rounded-2xl p-4 transition-all duration-200 {personalizationCookies ? 'bg-violet-50 ring-1 ring-inset ring-violet-200 dark:bg-violet-500/10 dark:ring-violet-500/20' : 'bg-slate-50 ring-1 ring-inset ring-slate-200 dark:bg-white/5 dark:ring-white/10' }"
>
<label class="flex cursor-pointer items-start justify-between gap-4">
<span class="flex min-w-0 items-start gap-3">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl transition-all duration-200 {personalizationCookies ? 'bg-violet-100 text-violet-700 dark:bg-violet-500/15 dark:text-violet-300' : 'bg-slate-200 text-slate-500 dark:bg-white/10 dark:text-slate-400' }"
>
<span class="icon-[lucide--sparkles] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="block text-sm font-bold text-slate-950 dark:text-white">
Personalization cookies
</span>
<span class="mt-1 block text-xs leading-5 text-slate-600 dark:text-slate-400">
Remember your choices and provide a more relevant website experience.
</span>
</span>
</span>
<span class="relative mt-0.5 shrink-0">
<input
type="checkbox"
name="personalization"
checked="{personalizationCookies}"
@change="personalizationCookies = event.target.checked"
class="peer sr-only"
/>
<span class="block h-6 w-11 rounded-full bg-slate-300 transition-colors duration-200 peer-checked:bg-violet-600 peer-focus-visible:ring-4 peer-focus-visible:ring-violet-500/20 dark:bg-slate-700" ></span>
<span class="absolute left-1 top-1 h-4 w-4 rounded-full bg-white shadow-sm transition-transform duration-200 peer-checked:translate-x-5" ></span>
</span>
</label>
</article>
<!-- Marketing -->
<article
class="rounded-2xl p-4 transition-all duration-200 {marketingCookies ? 'bg-rose-50 ring-1 ring-inset ring-rose-200 dark:bg-rose-500/10 dark:ring-rose-500/20' : 'bg-slate-50 ring-1 ring-inset ring-slate-200 dark:bg-white/5 dark:ring-white/10' }"
>
<label class="flex cursor-pointer items-start justify-between gap-4">
<span class="flex min-w-0 items-start gap-3">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl transition-all duration-200 {marketingCookies ? 'bg-rose-100 text-rose-700 dark:bg-rose-500/15 dark:text-rose-300' : 'bg-slate-200 text-slate-500 dark:bg-white/10 dark:text-slate-400' }"
>
<span class="icon-[lucide--megaphone] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="block text-sm font-bold text-slate-950 dark:text-white">
Marketing cookies
</span>
<span class="mt-1 block text-xs leading-5 text-slate-600 dark:text-slate-400">
Measure campaigns and show relevant WrNexus content across supported services.
</span>
</span>
</span>
<span class="relative mt-0.5 shrink-0">
<input
type="checkbox"
name="marketing"
checked="{marketingCookies}"
@change="marketingCookies = event.target.checked"
class="peer sr-only"
/>
<span class="block h-6 w-11 rounded-full bg-slate-300 transition-colors duration-200 peer-checked:bg-rose-600 peer-focus-visible:ring-4 peer-focus-visible:ring-rose-500/20 dark:bg-slate-700" ></span>
<span class="absolute left-1 top-1 h-4 w-4 rounded-full bg-white shadow-sm transition-transform duration-200 peer-checked:translate-x-5" ></span>
</span>
</label>
</article>
</div>
<!-- Modal footer -->
<footer
class="border-t border-slate-200 bg-slate-50 px-5 py-4 dark:border-white/10 dark:bg-white/[0.02] sm:px-6"
>
<div class="grid gap-2 sm:grid-cols-3">
<button
type="button"
@click=" analyticsCookies = false; personalizationCookies = false; marketingCookies = false "
class="inline-flex h-11 items-center justify-center rounded-xl border border-slate-200 bg-white px-4 text-sm font-semibold text-slate-700 transition hover:border-slate-300 hover:bg-slate-100 hover:text-slate-950 dark:border-white/10 dark:bg-white/5 dark:text-slate-200 dark:hover:bg-white/10 dark:hover:text-white"
>
Reject optional
</button>
<button
type="button"
@click=" analyticsCookies = true; personalizationCookies = true; marketingCookies = true "
class="inline-flex h-11 items-center justify-center rounded-xl border border-indigo-200 bg-indigo-50 px-4 text-sm font-semibold text-indigo-700 transition hover:border-indigo-300 hover:bg-indigo-100 dark:border-indigo-500/20 dark:bg-indigo-500/10 dark:text-indigo-300 dark:hover:bg-indigo-500/15"
>
Enable all
</button>
<button
type="button"
@click=" preferencesOpen = false; bannerOpen = false "
data-cookie-action="save-preferences"
class="inline-flex h-11 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-4 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl"
>
<span class="icon-[lucide--save] h-4 w-4" aria-hidden="true" ></span>
Save preferences
</button>
</div>
</footer>
</div>
</div>
</section>
</div>
}
}
@@ -0,0 +1,421 @@
component DevelopersMegaMenu {
props {
class: string = ""
}
view {
<div
class="border-t border-slate-200 bg-white shadow-2xl shadow-slate-950/10 dark:border-white/10 dark:bg-slate-950 dark:shadow-black/30 {class}"
>
<div class="mx-auto max-w-7xl px-6 py-8 lg:px-10">
<div class="grid gap-8 lg:grid-cols-[minmax(0,1.55fr)_minmax(18rem,0.8fr)]">
<!-- Developer resources -->
<section>
<div class="mb-5 flex items-end justify-between gap-6">
<div>
<p class="text-xs font-semibold uppercase tracking-[0.16em] text-indigo-600 dark:text-indigo-400">
Developers
</p>
<h2 class="mt-2 text-lg font-bold tracking-tight text-slate-950 dark:text-white">
Build secure identity into any application
</h2>
<p class="mt-1 text-sm text-slate-500 dark:text-slate-400">
Explore documentation, APIs, SDKs, examples, and tools built
for modern development teams.
</p>
</div>
<a
href="/docs"
class="group hidden items-center gap-2 text-sm font-semibold text-indigo-600 transition hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300 xl:inline-flex"
>
Open documentation
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
</div>
<div class="grid gap-2 md:grid-cols-2">
<!-- Documentation -->
<a
href="/docs"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-indigo-200 hover:bg-indigo-50 dark:hover:border-indigo-500/20 dark:hover:bg-indigo-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-indigo-100 text-indigo-700 transition group-hover:bg-indigo-600 group-hover:text-white dark:bg-indigo-500/15 dark:text-indigo-300 dark:group-hover:bg-indigo-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--book-open] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Documentation
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-indigo-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-indigo-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Guides for authentication, users, organizations, and
authorization.
</span>
</span>
</a>
<!-- API reference -->
<a
href="/docs/api"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-violet-200 hover:bg-violet-50 dark:hover:border-violet-500/20 dark:hover:bg-violet-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-violet-100 text-violet-700 transition group-hover:bg-violet-600 group-hover:text-white dark:bg-violet-500/15 dark:text-violet-300 dark:group-hover:bg-violet-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--braces] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
API reference
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-violet-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-violet-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Complete endpoint, request, response, and error
documentation.
</span>
</span>
</a>
<!-- SDKs -->
<a
href="/sdks"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-cyan-200 hover:bg-cyan-50 dark:hover:border-cyan-500/20 dark:hover:bg-cyan-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-cyan-100 text-cyan-700 transition group-hover:bg-cyan-600 group-hover:text-white dark:bg-cyan-500/15 dark:text-cyan-300 dark:group-hover:bg-cyan-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--package] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
SDKs
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-cyan-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-cyan-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Official libraries for JavaScript, TypeScript, Go, Java,
Python, and more.
</span>
</span>
</a>
<!-- Examples -->
<a
href="/examples"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-emerald-200 hover:bg-emerald-50 dark:hover:border-emerald-500/20 dark:hover:bg-emerald-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-emerald-100 text-emerald-700 transition group-hover:bg-emerald-600 group-hover:text-white dark:bg-emerald-500/15 dark:text-emerald-300 dark:group-hover:bg-emerald-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--flask-conical] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Examples
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-emerald-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-emerald-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Ready-to-run projects for common frameworks and identity
flows.
</span>
</span>
</a>
<!-- Webhooks -->
<a
href="/docs/webhooks"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-amber-200 hover:bg-amber-50 dark:hover:border-amber-500/20 dark:hover:bg-amber-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-amber-100 text-amber-700 transition group-hover:bg-amber-600 group-hover:text-white dark:bg-amber-500/15 dark:text-amber-300 dark:group-hover:bg-amber-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--webhook] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Webhooks
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-amber-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-amber-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Respond to user, session, organization, and security events.
</span>
</span>
</a>
<!-- Changelog -->
<a
href="/changelog"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-rose-200 hover:bg-rose-50 dark:hover:border-rose-500/20 dark:hover:bg-rose-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-rose-100 text-rose-700 transition group-hover:bg-rose-600 group-hover:text-white dark:bg-rose-500/15 dark:text-rose-300 dark:group-hover:bg-rose-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--history] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Changelog
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-rose-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-rose-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Review platform releases, improvements, and migration notes.
</span>
</span>
</a>
</div>
<a
href="/docs"
class="mt-4 flex items-center justify-center gap-2 rounded-xl border border-indigo-200 bg-indigo-50 px-4 py-3 text-sm font-semibold text-indigo-700 transition hover:border-indigo-300 hover:bg-indigo-100 dark:border-indigo-500/20 dark:bg-indigo-500/10 dark:text-indigo-300 dark:hover:border-indigo-500/30 dark:hover:bg-indigo-500/15 xl:hidden"
>
Open documentation
<span class="icon-[lucide--arrow-right] h-4 w-4" aria-hidden="true" ></span>
</a>
</section>
<!-- Developer toolkit -->
<aside
class="relative overflow-hidden rounded-3xl border border-indigo-200 bg-linear-to-br from-indigo-50 via-white to-violet-50 p-6 shadow-xl shadow-indigo-950/5 dark:border-indigo-500/20 dark:from-indigo-500/10 dark:via-slate-900 dark:to-violet-500/10 dark:shadow-black/20"
>
<!-- Decorative glow -->
<div aria-hidden="true" class="absolute -right-20 -top-20 h-48 w-48 rounded-full bg-indigo-300/30 blur-3xl dark:bg-indigo-500/10" ></div>
<div aria-hidden="true" class="absolute -bottom-24 -left-20 h-48 w-48 rounded-full bg-violet-300/30 blur-3xl dark:bg-violet-500/10" ></div>
<div class="relative">
<!-- Header -->
<div class="flex items-center justify-between gap-4">
<span
class="inline-flex items-center gap-2 rounded-full border border-indigo-200 bg-white/80 px-3 py-1.5 text-xs font-semibold text-indigo-700 backdrop-blur-sm dark:border-indigo-500/20 dark:bg-white/5 dark:text-indigo-300"
>
<span class="icon-[lucide--code-2] h-3.5 w-3.5" aria-hidden="true" ></span>
Developer toolkit
</span>
<span class="inline-flex items-center gap-2 text-xs font-medium text-slate-500 dark:text-slate-400">
<span class="relative flex h-2 w-2">
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-emerald-400 opacity-60"></span>
<span class="relative inline-flex h-2 w-2 rounded-full bg-emerald-500"></span>
</span>
APIs operational
</span>
</div>
<!-- Intro -->
<div
class="mt-5 grid h-12 w-12 place-items-center rounded-2xl bg-indigo-600 text-white shadow-lg shadow-indigo-600/20"
>
<span class="icon-[lucide--blocks] h-6 w-6" aria-hidden="true" ></span>
</div>
<h3 class="mt-5 text-xl font-bold tracking-tight text-slate-950 dark:text-white">
Everything developers need
</h3>
<p class="mt-3 text-sm leading-6 text-slate-600 dark:text-slate-400">
Build, test, and ship secure identity flows with APIs, SDKs,
examples, webhooks, and complete documentation.
</p>
<!-- Toolkit links -->
<div class="mt-6 grid gap-3">
<!-- Documentation -->
<a
href="/docs"
class="group flex items-center justify-between gap-4 rounded-2xl border border-slate-200 bg-white/80 px-4 py-3.5 transition hover:border-indigo-300 hover:bg-white hover:shadow-md dark:border-white/10 dark:bg-white/5 dark:hover:border-indigo-500/30 dark:hover:bg-white/10"
>
<span class="flex items-center gap-3">
<span
class="grid h-9 w-9 shrink-0 place-items-center rounded-xl bg-indigo-100 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300"
>
<span class="icon-[lucide--book-open] h-4.5 w-4.5" aria-hidden="true" ></span>
</span>
<span>
<span class="block text-sm font-semibold text-slate-950 dark:text-white">
Documentation
</span>
<span class="mt-0.5 block text-xs text-slate-500 dark:text-slate-400">
Guides and platform concepts
</span>
</span>
</span>
<span class="icon-[lucide--arrow-up-right] h-4 w-4 shrink-0 text-slate-400 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-indigo-600 dark:group-hover:text-indigo-300" aria-hidden="true" ></span>
</a>
<!-- API reference -->
<a
href="/docs/api"
class="group flex items-center justify-between gap-4 rounded-2xl border border-slate-200 bg-white/80 px-4 py-3.5 transition hover:border-violet-300 hover:bg-white hover:shadow-md dark:border-white/10 dark:bg-white/5 dark:hover:border-violet-500/30 dark:hover:bg-white/10"
>
<span class="flex items-center gap-3">
<span
class="grid h-9 w-9 shrink-0 place-items-center rounded-xl bg-violet-100 text-violet-700 dark:bg-violet-500/15 dark:text-violet-300"
>
<span class="icon-[lucide--braces] h-4.5 w-4.5" aria-hidden="true" ></span>
</span>
<span>
<span class="block text-sm font-semibold text-slate-950 dark:text-white">
API reference
</span>
<span class="mt-0.5 block text-xs text-slate-500 dark:text-slate-400">
Endpoints, requests, and responses
</span>
</span>
</span>
<span class="icon-[lucide--arrow-up-right] h-4 w-4 shrink-0 text-slate-400 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-violet-600 dark:group-hover:text-violet-300" aria-hidden="true" ></span>
</a>
<!-- SDKs -->
<a
href="/sdks"
class="group flex items-center justify-between gap-4 rounded-2xl border border-slate-200 bg-white/80 px-4 py-3.5 transition hover:border-cyan-300 hover:bg-white hover:shadow-md dark:border-white/10 dark:bg-white/5 dark:hover:border-cyan-500/30 dark:hover:bg-white/10"
>
<span class="flex items-center gap-3">
<span
class="grid h-9 w-9 shrink-0 place-items-center rounded-xl bg-cyan-100 text-cyan-700 dark:bg-cyan-500/15 dark:text-cyan-300"
>
<span class="icon-[lucide--package] h-4.5 w-4.5" aria-hidden="true" ></span>
</span>
<span>
<span class="block text-sm font-semibold text-slate-950 dark:text-white">
Official SDKs
</span>
<span class="mt-0.5 block text-xs text-slate-500 dark:text-slate-400">
Libraries for your preferred stack
</span>
</span>
</span>
<span class="icon-[lucide--arrow-up-right] h-4 w-4 shrink-0 text-slate-400 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-cyan-600 dark:group-hover:text-cyan-300" aria-hidden="true" ></span>
</a>
</div>
<!-- Actions -->
<div class="mt-6 grid grid-cols-2 gap-3">
<a
href="/docs/quickstart"
class="group flex h-11 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-4 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900"
>
Start building
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
<a
href="/examples"
class="flex h-11 items-center justify-center gap-2 rounded-xl border border-slate-200 bg-white/80 px-4 text-sm font-semibold text-slate-700 transition hover:border-indigo-300 hover:bg-white hover:text-indigo-700 dark:border-white/10 dark:bg-white/5 dark:text-slate-200 dark:hover:border-indigo-500/30 dark:hover:bg-white/10 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--flask-conical] h-4 w-4" aria-hidden="true" ></span>
Examples
</a>
</div>
</div>
</aside>
</div>
<!-- Bottom developer links -->
<div
class="mt-7 flex flex-wrap items-center justify-between gap-4 border-t border-slate-200 pt-5 dark:border-white/10"
>
<div class="flex flex-wrap items-center gap-x-6 gap-y-3">
<a
href="/docs/authentication"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--fingerprint] h-4 w-4" aria-hidden="true" ></span>
Authentication guides
</a>
<a
href="/docs/organizations"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--building-2] h-4 w-4" aria-hidden="true" ></span>
Organizations
</a>
<a
href="/docs/authorization"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--shield-keyhole] h-4 w-4" aria-hidden="true" ></span>
Authorization
</a>
<a
href="/status"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="relative flex h-2 w-2">
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-emerald-400 opacity-60"></span>
<span class="relative inline-flex h-2 w-2 rounded-full bg-emerald-500"></span>
</span>
System status
</a>
</div>
<a
href="/support/developers"
class="group inline-flex items-center gap-2 text-sm font-semibold text-indigo-600 transition hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300"
>
Developer support
<span class="icon-[lucide--arrow-up-right] h-4 w-4 transition-transform group-hover:translate-x-0.5 group-hover:-translate-y-0.5" aria-hidden="true" ></span>
</a>
</div>
</div>
</div>
}
}
+165 -11
View File
@@ -1,13 +1,167 @@
component EmptyState {
props {
class = ""
icon = ""
title = "Nothing here yet"
description = ""
actionLabel = ""
actionHref = "#"
}
view {
<div class="rounded-3xl border border-dashed border-slate-300 text-center dark:border-slate-700 {class}"><div class="mx-auto flex size-14 items-center justify-center rounded-2xl bg-violet-100 text-2xl text-violet-700 dark:bg-violet-950 dark:text-violet-300">{icon}</div><h3 class="mt-4 text-lg font-bold">{title}</h3><p data-show="description" class="mx-auto mt-2 max-w-md text-sm text-slate-500">{description}</p><a data-show="actionLabel" href="{actionHref}" class="mt-5 inline-flex min-h-11 items-center rounded-xl bg-violet-600 px-4 py-2.5 text-sm font-semibold text-white">{actionLabel}</a></div>
}
props {
class: string = ""
eyebrow = ""
title = "Nothing found"
description = ""
iconClass = "icon-[lucide--inbox]"
primaryLabel = ""
primaryHref = ""
secondaryLabel = ""
secondaryHref = ""
compact = false
centered = true
suggestions = []
}
view {
<section
aria-labelledby="empty-state-title"
class="w-full {class}"
class:py-6="compact"
class:sm:py-8="compact"
class:py-8="!compact"
class:sm:py-10="!compact"
>
<div
class=" relative mx-auto w-full overflow-hidden rounded-3xl border border-indigo-200/80 bg-linear-to-br from-indigo-50/90 via-white to-violet-50/80 shadow-xl shadow-indigo-950/5 dark:border-indigo-500/20 dark:from-indigo-500/10 dark:via-slate-900 dark:to-violet-500/10 dark:shadow-black/20 "
class:max-w-3xl="centered"
>
<!-- Background decoration -->
<div
aria-hidden="true"
class="pointer-events-none absolute inset-0 overflow-hidden"
>
<div class="absolute -right-20 -top-24 h-56 w-56 rounded-full bg-indigo-200/30 blur-3xl dark:bg-indigo-500/10"></div>
<div class="absolute -bottom-24 -left-20 h-56 w-56 rounded-full bg-violet-200/20 blur-3xl dark:bg-violet-500/5"></div>
<div class="absolute inset-x-0 top-0 h-px bg-linear-to-r from-transparent via-indigo-300/70 to-transparent dark:via-indigo-500/20"></div>
</div>
<div
class="relative"
class:px-6="compact"
class:py-8="compact"
class:sm:px-8="compact"
class:sm:py-9="compact"
class:px-6="!compact"
class:py-10="!compact"
class:sm:px-10="!compact"
class:sm:py-12="!compact"
class:lg:px-12="!compact"
class:text-center="centered"
>
<div
class=" {compact ? 'max-w-xl' : 'max-w-2xl'} {centered ? 'mx-auto' : 'mr-auto'} "
class:max-w-xl="compact"
class:max-w-2xl="!compact"
>
<!-- Icon -->
<div
class="flex"
class:justify-center="centered"
>
<span
class="grid place-items-center rounded-2xl bg-indigo-50 text-indigo-600 ring-1 ring-inset ring-indigo-200 dark:bg-indigo-500/10 dark:text-indigo-300 dark:ring-indigo-500/20"
class:h-12="compact"
class:w-12="compact"
class:h-14="!compact"
class:w-14="!compact"
>
<span class="{iconClass}" class:h-5="compact" class:w-5="compact" class:h-6="!compact" class:w-6="!compact" aria-hidden="true" ></span>
</span>
</div>
<!-- Eyebrow -->
<p
class="mt-5 text-xs font-bold uppercase tracking-[0.18em] text-indigo-600 dark:text-indigo-400"
class:hidden="eyebrow === ''"
>
{eyebrow}
</p>
<!-- Title -->
<h2
id="empty-state-title"
class="font-bold tracking-tight text-slate-950 dark:text-white"
class:mt-5="eyebrow === ''"
class:mt-3="eyebrow !== ''"
class:text-xl="compact"
class:leading-tight="compact"
class:sm:text-2xl="compact"
class:text-2xl="!compact"
class:leading-tight="!compact"
class:sm:text-3xl="!compact"
class:lg:text-[2rem]="!compact"
>
{title}
</h2>
<!-- Description -->
<p
class="mx-auto mt-3 max-w-xl text-sm leading-6 text-slate-600 dark:text-slate-400"
class:hidden="description === ''"
class:sm:text-base="!compact"
class:sm:leading-7="!compact"
>
{description}
</p>
<!-- Suggestions -->
<div
class="mt-6 flex flex-wrap gap-2"
class:hidden="suggestions.length === 0"
class:justify-center="centered"
>
{#each suggestions.slice(0, 4) as suggestion}
<a
href="{suggestion.href}"
class="inline-flex items-center gap-2 rounded-full border border-slate-200 bg-white px-3 py-2 text-xs font-semibold text-slate-600 shadow-sm transition hover:-translate-y-0.5 hover:border-indigo-300 hover:bg-indigo-50 hover:text-indigo-700 dark:border-white/10 dark:bg-white/5 dark:text-slate-300 dark:hover:border-indigo-500/30 dark:hover:bg-indigo-500/10 dark:hover:text-indigo-300"
>
<span class="{suggestion.iconClass} h-3.5 w-3.5" aria-hidden="true" ></span>
{suggestion.label}
</a>
{/each}
</div>
<!-- Actions -->
<div
class="flex flex-col gap-3 sm:flex-row"
class:mt-7="suggestions.length === 0"
class:mt-6="suggestions.length > 0"
class:hidden="primaryLabel === '' && secondaryLabel === ''"
class:justify-center="centered"
>
<a
href="{secondaryHref}"
class="h-11 items-center justify-center rounded-xl border border-slate-200 bg-white px-4 text-sm font-semibold text-slate-700 shadow-sm transition hover:-translate-y-0.5 hover:border-indigo-300 hover:bg-slate-50 hover:text-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-200 dark:hover:border-indigo-500/30 dark:hover:bg-white/10 dark:hover:text-indigo-300"
class:hidden="secondaryLabel === ''"
class:inline-flex="secondaryLabel !== ''"
>
{secondaryLabel}
</a>
<a
href="{primaryHref}"
class="group h-11 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-4 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900"
class:hidden="primaryLabel === ''"
class:inline-flex="primaryLabel !== ''"
>
{primaryLabel}
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
</div>
</div>
</div>
</div>
</section>
}
}
+191 -11
View File
@@ -1,13 +1,193 @@
component ErrorState {
props {
class = ""
code = "500"
title = "Something went wrong"
description = "Please try again."
actionLabel = "Try again"
actionHref = "/"
}
view {
<div class="max-w-2xl text-center {class}"><div class="text-8xl font-black text-violet-100 dark:text-violet-950">{code}</div><h1 class="-mt-4 text-3xl font-black">{title}</h1><p class="mt-4 text-slate-500">{description}</p><a href="{actionHref}" class="mt-8 inline-flex min-h-12 items-center rounded-xl bg-violet-600 px-5 font-semibold text-white">{actionLabel}</a></div>
}
props {
class: string = ""
type = "error"
eyebrow = ""
title = "Something went wrong"
description = ""
errorCode = ""
iconClass = ""
primaryLabel = ""
primaryHref = ""
secondaryLabel = ""
secondaryHref = ""
retryLabel = ""
retryAction = ""
centered = true
compact = false
details = []
}
view {
<section
aria-labelledby="error-state-title"
role="{type === 'error' ? 'alert' : 'status'}"
class="w-full {class}"
class:py-6="compact"
class:sm:py-8="compact"
class:py-8="!compact"
class:sm:py-10="!compact"
>
<div
class=" relative w-full overflow-hidden rounded-3xl border shadow-xl {type === 'warning' ? 'border-amber-200/80 bg-linear-to-br from-amber-50 via-white to-orange-50 shadow-amber-950/5 dark:border-amber-500/20 dark:from-amber-500/10 dark:via-slate-900 dark:to-orange-500/10' : type === 'forbidden' ? 'border-violet-200/80 bg-linear-to-br from-violet-50 via-white to-indigo-50 shadow-violet-950/5 dark:border-violet-500/20 dark:from-violet-500/10 dark:via-slate-900 dark:to-indigo-500/10' : type === 'offline' ? 'border-slate-200 bg-linear-to-br from-slate-100 via-white to-indigo-50 shadow-slate-950/5 dark:border-white/10 dark:from-white/5 dark:via-slate-900 dark:to-indigo-500/10' : 'border-rose-200/80 bg-linear-to-br from-rose-50 via-white to-orange-50 shadow-rose-950/5 dark:border-rose-500/20 dark:from-rose-500/10 dark:via-slate-900 dark:to-orange-500/10' } "
class:mx-auto="centered"
class:max-w-3xl="centered"
>
<!-- Decorative background -->
<div
aria-hidden="true"
class="pointer-events-none absolute inset-0 overflow-hidden"
>
<div class=" absolute -right-20 -top-24 h-64 w-64 rounded-full blur-3xl {type === 'warning' ? 'bg-amber-300/25 dark:bg-amber-500/10' : type === 'forbidden' ? 'bg-violet-300/25 dark:bg-violet-500/10' : type === 'offline' ? 'bg-slate-300/25 dark:bg-slate-500/10' : 'bg-rose-300/25 dark:bg-rose-500/10' } " ></div>
<div class=" absolute -bottom-28 -left-20 h-64 w-64 rounded-full blur-3xl {type === 'warning' ? 'bg-orange-300/20 dark:bg-orange-500/10' : type === 'forbidden' ? 'bg-indigo-300/20 dark:bg-indigo-500/10' : type === 'offline' ? 'bg-indigo-300/15 dark:bg-indigo-500/5' : 'bg-orange-300/20 dark:bg-orange-500/10' } " ></div>
<div class=" absolute left-1/2 top-0 h-px w-2/3 -translate-x-1/2 bg-linear-to-r from-transparent to-transparent {type === 'warning' ? 'via-amber-400/60 dark:via-amber-400/20' : type === 'forbidden' ? 'via-violet-400/60 dark:via-violet-400/20' : type === 'offline' ? 'via-slate-400/50 dark:via-slate-400/20' : 'via-rose-400/60 dark:via-rose-400/20' } " ></div>
</div>
<div
class="relative"
class:px-6="compact"
class:py-8="compact"
class:sm:px-8="compact"
class:sm:py-9="compact"
class:px-6="!compact"
class:py-10="!compact"
class:sm:px-10="!compact"
class:sm:py-12="!compact"
class:lg:px-12="!compact"
class:text-center="centered"
class:text-left="!centered"
>
<div class="{compact ? 'max-w-xl' : 'max-w-2xl'} {centered ? 'mx-auto' : 'mr-auto'}" >
<!-- Icon -->
<div
class="flex"
class:justify-center="centered"
class:justify-start="!centered"
>
<div class="relative">
<div aria-hidden="true" class=" absolute inset-1 rounded-2xl blur-xl {type === 'warning' ? 'bg-amber-500/25' : type === 'forbidden' ? 'bg-violet-500/25' : type === 'offline' ? 'bg-slate-500/20' : 'bg-rose-500/25' } " ></div>
<span
class=" relative grid place-items-center rounded-2xl border bg-white/90 shadow-lg backdrop-blur-sm {compact ? 'h-12 w-12' : 'h-14 w-14'} {type === 'warning' ? 'border-amber-200 text-amber-700 shadow-amber-950/10 dark:border-amber-500/20 dark:bg-amber-500/10 dark:text-amber-300' : type === 'forbidden' ? 'border-violet-200 text-violet-700 shadow-violet-950/10 dark:border-violet-500/20 dark:bg-violet-500/10 dark:text-violet-300' : type === 'offline' ? 'border-slate-200 text-slate-700 shadow-slate-950/10 dark:border-white/10 dark:bg-white/5 dark:text-slate-300' : 'border-rose-200 text-rose-700 shadow-rose-950/10 dark:border-rose-500/20 dark:bg-rose-500/10 dark:text-rose-300' } "
>
<span class=" {compact ? 'h-5 w-5' : 'h-6 w-6'} {iconClass !== '' ? iconClass : type === 'warning' ? 'icon-[lucide--triangle-alert]' : type === 'forbidden' ? 'icon-[lucide--shield-alert]' : type === 'offline' ? 'icon-[lucide--wifi-off]' : 'icon-[lucide--circle-x]' } " aria-hidden="true" ></span>
</span>
</div>
</div>
<!-- Error code -->
<div
class="mt-5 flex"
class:hidden="errorCode === ''"
class:justify-center="centered"
class:justify-start="!centered"
>
<span
class=" rounded-full px-3 py-1 text-xs font-bold tracking-[0.14em] {type === 'warning' ? 'bg-amber-100 text-amber-700 dark:bg-amber-500/15 dark:text-amber-300' : type === 'forbidden' ? 'bg-violet-100 text-violet-700 dark:bg-violet-500/15 dark:text-violet-300' : type === 'offline' ? 'bg-slate-200 text-slate-700 dark:bg-white/10 dark:text-slate-300' : 'bg-rose-100 text-rose-700 dark:bg-rose-500/15 dark:text-rose-300' } "
>
{errorCode}
</span>
</div>
<!-- Eyebrow -->
<p
class=" text-xs font-bold uppercase tracking-[0.18em] {errorCode !== '' ? 'mt-4' : 'mt-5'} {type === 'warning' ? 'text-amber-700 dark:text-amber-400' : type === 'forbidden' ? 'text-violet-700 dark:text-violet-400' : type === 'offline' ? 'text-slate-600 dark:text-slate-400' : 'text-rose-700 dark:text-rose-400' } "
class:hidden="eyebrow === ''"
>
{eyebrow}
</p>
<!-- Title -->
<h2
id="error-state-title"
class=" font-bold leading-tight tracking-tight text-slate-950 dark:text-white {eyebrow !== '' || errorCode !== '' ? 'mt-3' : 'mt-5'} {compact ? 'text-xl sm:text-2xl' : 'text-2xl sm:text-3xl lg:text-[2rem]'} "
>
{title}
</h2>
<!-- Description -->
<p
class=" mt-3 max-w-xl text-sm leading-6 text-slate-600 dark:text-slate-400 {!compact ? 'sm:text-base sm:leading-7' : ''} {centered ? 'mx-auto' : 'mr-auto'} "
class:hidden="description === ''"
>
{description}
</p>
<!-- Details -->
<div
class="mt-6 flex flex-wrap gap-x-5 gap-y-3"
class:hidden="details.length === 0"
class:justify-center="centered"
class:justify-start="!centered"
>
{#each details.slice(0, 4) as detail}
<div class="inline-flex items-center gap-2 text-sm font-medium text-slate-600 dark:text-slate-300">
<span
class=" grid h-6 w-6 shrink-0 place-items-center rounded-full {type === 'warning' ? 'bg-amber-100 text-amber-700 dark:bg-amber-500/15 dark:text-amber-300' : type === 'forbidden' ? 'bg-violet-100 text-violet-700 dark:bg-violet-500/15 dark:text-violet-300' : type === 'offline' ? 'bg-slate-200 text-slate-700 dark:bg-white/10 dark:text-slate-300' : 'bg-rose-100 text-rose-700 dark:bg-rose-500/15 dark:text-rose-300' } "
>
<span class="{detail.iconClass} h-3.5 w-3.5" aria-hidden="true" ></span>
</span>
{detail.label}
</div>
{/each}
</div>
<!-- Actions -->
<div
class="mt-7 flex flex-col gap-3 sm:flex-row"
class:hidden="primaryLabel === '' && secondaryLabel === '' && retryLabel === ''"
class:justify-center="centered"
class:justify-start="!centered"
>
<!-- Secondary -->
<a
href="{secondaryHref}"
class="h-11 items-center justify-center rounded-xl border border-slate-200 bg-white/90 px-4 text-sm font-semibold text-slate-700 shadow-sm backdrop-blur-sm transition hover:-translate-y-0.5 hover:border-indigo-300 hover:bg-white hover:text-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-200 dark:hover:border-indigo-500/30 dark:hover:bg-white/10 dark:hover:text-indigo-300"
class:hidden="secondaryLabel === ''"
class:inline-flex="secondaryLabel !== ''"
>
{secondaryLabel}
</a>
<!-- Retry -->
<button
type="button"
@click="{retryAction}"
class=" group h-11 items-center justify-center gap-2 rounded-xl px-4 text-sm font-bold text-white shadow-lg transition hover:-translate-y-0.5 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 {type === 'warning' ? 'bg-amber-600 shadow-amber-600/20 hover:bg-amber-500 focus-visible:ring-amber-500' : type === 'forbidden' ? 'bg-violet-600 shadow-violet-600/20 hover:bg-violet-500 focus-visible:ring-violet-500' : type === 'offline' ? 'bg-slate-800 shadow-slate-900/20 hover:bg-slate-700 focus-visible:ring-slate-500 dark:bg-slate-200 dark:text-slate-950 dark:hover:bg-white' : 'bg-rose-600 shadow-rose-600/20 hover:bg-rose-500 focus-visible:ring-rose-500' } "
class:hidden="retryLabel === ''"
class:inline-flex="retryLabel !== ''"
>
<span class="icon-[lucide--rotate-cw] h-4 w-4 transition-transform group-hover:rotate-45" aria-hidden="true" ></span>
{retryLabel}
</button>
<!-- Primary link -->
<a
href="{primaryHref}"
class=" group h-11 items-center justify-center gap-2 rounded-xl px-4 text-sm font-bold text-white shadow-lg transition hover:-translate-y-0.5 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 {type === 'warning' ? 'bg-amber-600 shadow-amber-600/20 hover:bg-amber-500 focus-visible:ring-amber-500' : type === 'forbidden' ? 'bg-violet-600 shadow-violet-600/20 hover:bg-violet-500 focus-visible:ring-violet-500' : type === 'offline' ? 'bg-slate-800 shadow-slate-900/20 hover:bg-slate-700 focus-visible:ring-slate-500 dark:bg-slate-200 dark:text-slate-950 dark:hover:bg-white' : 'bg-rose-600 shadow-rose-600/20 hover:bg-rose-500 focus-visible:ring-rose-500' } "
class:hidden="primaryLabel === ''"
class:inline-flex="primaryLabel !== ''"
>
{primaryLabel}
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
</div>
</div>
</div>
</div>
</section>
}
}
+148 -7
View File
@@ -1,9 +1,150 @@
component LogoCloud {
props {
class = ""
title = "Trusted by modern teams"
}
view {
<section class="{class}" aria-label="{title}"><p class="text-center text-sm font-semibold uppercase tracking-wider text-slate-500">{title}</p><div class="mt-6 flex flex-wrap items-center justify-center gap-x-10 gap-y-6 opacity-70 grayscale transition hover:grayscale-0"><slot /></div></section>
}
props {
class: string = ""
eyebrow = ""
title = "Trusted by teams building modern products"
description = ""
logos = []
centered = true
compact = false
variant = "strip"
grayscale = true
showNames = false
maxItems = 8
}
view {
<section
aria-labelledby="logo-cloud-title"
class="w-full px-5 sm:px-8 lg:px-10 xl:px-12 {class}"
class:py-10="compact"
class:sm:py-12="compact"
class:py-14="!compact"
class:sm:py-16="!compact"
class:lg:py-20="!compact"
class:hidden="logos.length === 0"
>
<div
class=" relative mx-auto w-full max-w-7xl overflow-hidden {variant === 'card' ? 'rounded-3xl border border-indigo-200/80 bg-linear-to-br from-indigo-50/80 via-white to-violet-50/70 shadow-xl shadow-indigo-950/5 dark:border-indigo-500/20 dark:from-indigo-500/10 dark:via-slate-900 dark:to-violet-500/10 dark:shadow-black/20' : '' } "
>
<!-- Decorative background -->
<div
aria-hidden="true"
class="pointer-events-none absolute inset-0 overflow-hidden"
class:hidden="variant !== 'card'"
>
<div class="absolute -right-24 -top-28 h-64 w-64 rounded-full bg-indigo-300/20 blur-3xl dark:bg-indigo-500/10"></div>
<div class="absolute -bottom-28 -left-24 h-64 w-64 rounded-full bg-violet-300/15 blur-3xl dark:bg-violet-500/5"></div>
<div class="absolute left-1/2 top-0 h-px w-2/3 -translate-x-1/2 bg-linear-to-r from-transparent via-indigo-400/60 to-transparent dark:via-indigo-400/20"></div>
</div>
<div
class="relative"
class:px-6="compact"
class:py-8="compact"
class:sm:px-8="compact"
class:sm:py-10="compact"
class:px-7="!compact"
class:py-10="!compact"
class:sm:px-10="!compact"
class:sm:py-12="!compact"
class:lg:px-12="!compact"
class:lg:py-14="!compact"
>
<!-- Heading -->
<div
class="max-w-3xl"
class:mx-auto="centered"
class:text-center="centered"
>
<p
class="text-xs font-bold uppercase tracking-[0.18em] text-indigo-600 dark:text-indigo-400"
class:hidden="eyebrow === ''"
>
{eyebrow}
</p>
<h2
id="logo-cloud-title"
class=" font-bold leading-tight tracking-tight text-slate-950 dark:text-white {eyebrow !== '' ? 'mt-3' : ''} {compact ? 'text-2xl sm:text-3xl' : 'text-3xl sm:text-4xl' } "
>
{title}
</h2>
<p
class=" mt-4 text-sm leading-7 text-slate-600 dark:text-slate-400 {centered ? 'mx-auto max-w-2xl' : 'max-w-2xl'} {!compact ? 'sm:text-base' : ''} "
class:hidden="description === ''"
>
{description}
</p>
</div>
<!-- Dynamic logo list -->
<div
class=" mt-8 flex flex-wrap justify-center {variant === 'cards' ? 'gap-4 sm:mt-10' : 'items-center gap-x-10 gap-y-7 sm:mt-10 lg:gap-x-14' } "
>
{#each logos.slice(0, maxItems) as logo}
<a
href="{logo.href}"
target="{logo.external ? '_blank' : '_self'}"
rel="{logo.external ? 'noopener noreferrer' : ''}"
aria-label="{logo.name}"
class=" group min-w-0 transition duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 {variant === 'cards' ? 'flex w-full items-center gap-4 rounded-2xl border border-slate-200/80 bg-white/80 p-4 shadow-sm backdrop-blur-sm hover:-translate-y-1 hover:border-indigo-300 hover:bg-white hover:shadow-lg dark:border-white/10 dark:bg-white/5 dark:hover:border-indigo-500/30 dark:hover:bg-white/10 sm:w-[calc(50%-0.5rem)] lg:w-[calc(25%-0.75rem)]' : 'flex h-12 items-center justify-center px-2 opacity-70 hover:opacity-100' } "
>
<!-- Logo mark -->
<div
class=" flex shrink-0 items-center justify-center {variant === 'cards' ? 'h-12 w-12 rounded-xl bg-slate-50 ring-1 ring-inset ring-slate-200 dark:bg-white/5 dark:ring-white/10' : '' } "
>
<img
src="{logo.src}"
alt="{logo.alt !== '' ? logo.alt : logo.name}"
loading="lazy"
class=" block object-contain transition duration-200 {variant === 'cards' ? 'max-h-7 max-w-9' : 'max-h-8 max-w-36' } {grayscale ? 'grayscale group-hover:grayscale-0' : '' } "
/>
<!-- Fallback mark -->
<span
class=" hidden h-8 w-8 items-center justify-center rounded-lg bg-indigo-50 text-sm font-bold text-indigo-700 dark:bg-indigo-500/10 dark:text-indigo-300 "
>
{logo.name.slice(0, 1)}
</span>
</div>
<!-- Partner text -->
<div
class="min-w-0"
class:hidden="variant !== 'cards' && !showNames"
>
<p class="truncate text-sm font-semibold text-slate-800 dark:text-slate-200">
{logo.name}
</p>
<p
class="mt-0.5 truncate text-xs text-slate-500 dark:text-slate-400"
class:hidden="logo.description === ''"
>
{logo.description}
</p>
</div>
</a>
{/each}
</div>
<!-- Optional count -->
<p
class="mt-6 text-sm text-slate-500 dark:text-slate-400"
class:hidden="logos.length <= maxItems"
class:text-center="centered"
>
And {logos.length - maxItems} more teams using WrNexus.
</p>
</div>
</div>
</section>
}
}
+212 -11
View File
@@ -1,13 +1,214 @@
component NewsletterForm {
props {
eyebrow = ""
title = ""
description = ""
href = ""
actionLabel = "Learn more"
image = ""
alt = ""
class = ""
}
view { <article class="wire-card wire-catalog-card {class}"><img data-show="image" src="{image}" alt="{alt}" class="wire-catalog-card__image" /><span data-show="eyebrow" class="wire-eyebrow">{eyebrow}</span><h3 data-show="title">{title}</h3><p data-show="description">{description}</p><slot /><a data-show="href" href="{href}" class="wire-btn wire-btn--ghost">{actionLabel}</a></article> }
props {
class: string = ""
eyebrow = ""
title = "Stay up to date"
description = ""
placeholder = "Enter your email"
buttonLabel = "Subscribe"
action = "/newsletter/subscribe"
method = "post"
emailName = "email"
privacyLabel = ""
privacyHref = "/privacy"
successMessage = "You are subscribed."
errorMessage = "Something went wrong. Please try again."
iconClass = "icon-[lucide--mail]"
centered = false
compact = false
showIcon = true
showPrivacy = true
fullWidth = false
}
state email = ""
state status = "idle"
state message = ""
view {
<section
aria-labelledby="newsletter-form-title"
class="w-full px-5 sm:px-8 lg:px-10 xl:px-12 {class}"
>
<div
class=" relative mx-auto w-full overflow-hidden rounded-3xl border border-indigo-200/80 bg-linear-to-br from-indigo-50 via-white to-violet-50 shadow-xl shadow-indigo-950/5 dark:border-indigo-500/20 dark:from-indigo-500/10 dark:via-slate-900 dark:to-violet-500/10 dark:shadow-black/20 "
class:max-w-7xl="fullWidth"
class:max-w-3xl="!fullWidth"
>
<!-- Background decoration -->
<div
aria-hidden="true"
class="pointer-events-none absolute inset-0 overflow-hidden"
>
<div class="absolute -right-20 -top-24 h-64 w-64 rounded-full bg-indigo-300/25 blur-3xl dark:bg-indigo-500/10"></div>
<div class="absolute -bottom-28 -left-20 h-64 w-64 rounded-full bg-violet-300/20 blur-3xl dark:bg-violet-500/10"></div>
<div class="absolute left-1/2 top-0 h-px w-2/3 -translate-x-1/2 bg-linear-to-r from-transparent via-indigo-400/60 to-transparent dark:via-indigo-400/20"></div>
</div>
<div
class="relative"
class:p-5="compact"
class:sm:p-6="compact"
class:p-7="!compact"
class:sm:p-9="!compact"
class:lg:p-10="!compact"
>
<div
class=" {compact ? 'flex flex-col gap-5 lg:flex-row lg:items-center lg:justify-between lg:gap-10' : centered ? 'mx-auto flex max-w-2xl flex-col items-center gap-7 text-center' : 'grid gap-8 lg:grid-cols-[minmax(0,1fr)_minmax(360px,480px)] lg:items-center lg:gap-12' } "
>
<!-- Content -->
<div
class=" min-w-0 {compact ? 'max-w-2xl' : ''} {centered && !compact ? 'w-full max-w-2xl' : ''} "
>
<div class=" flex gap-4 {centered && !compact ? 'flex-col items-center' : 'items-start' } " >
<!-- Icon -->
<div
class="relative shrink-0"
class:hidden="!showIcon"
>
<div aria-hidden="true" class="absolute inset-1 rounded-2xl bg-indigo-500/25 blur-xl" ></div>
<span
class=" relative grid place-items-center rounded-2xl border border-indigo-200 bg-white/90 text-indigo-600 shadow-lg shadow-indigo-950/10 backdrop-blur-sm dark:border-indigo-500/20 dark:bg-indigo-500/10 dark:text-indigo-300 dark:shadow-black/20 {compact ? 'h-11 w-11' : 'h-13 w-13'} "
>
<span class=" {iconClass} {compact ? 'h-5 w-5' : 'h-6 w-6'} " aria-hidden="true" ></span>
</span>
</div>
<div class="min-w-0">
<!-- Eyebrow -->
<p
class="text-xs font-bold uppercase tracking-[0.18em] text-indigo-600 dark:text-indigo-400"
class:hidden="eyebrow === ''"
>
{eyebrow}
</p>
<!-- Title -->
<h2
id="newsletter-form-title"
class=" font-bold leading-tight tracking-tight text-slate-950 dark:text-white {eyebrow !== '' ? 'mt-2' : ''} {compact ? 'text-xl sm:text-2xl' : 'text-2xl sm:text-3xl' } "
>
{title}
</h2>
</div>
</div>
<!-- Description -->
<p
class=" mt-4 text-sm leading-6 text-slate-600 dark:text-slate-400 {!compact ? 'sm:text-base sm:leading-7' : ''} {centered && !compact ? 'mx-auto max-w-xl' : 'max-w-2xl' } "
class:hidden="description === ''"
>
{description}
</p>
</div>
<!-- Form -->
<div class=" w-full {compact ? 'lg:max-w-xl' : ''} {centered && !compact ? 'max-w-2xl' : ''} " >
<form
action="{action}"
method="{method}"
class="w-full"
@submit=" status = 'loading'; message = ''; "
>
<label
for="newsletter-email"
class="sr-only"
>
Email address
</label>
<div
class=" flex w-full flex-col gap-2 rounded-2xl border border-slate-200 bg-white/85 p-2 shadow-sm backdrop-blur-sm dark:border-white/10 dark:bg-white/5 sm:flex-row "
>
<div class="relative min-w-0 flex-1">
<span class="pointer-events-none absolute left-3.5 top-1/2 h-4.5 w-4.5 -translate-y-1/2 text-slate-400 icon-[lucide--mail] dark:text-slate-500" aria-hidden="true" ></span>
<input
id="newsletter-email"
type="email"
name="{emailName}"
value="{email}"
placeholder="{placeholder}"
autocomplete="email"
required
@input="email = event.target.value"
class=" w-full rounded-xl bg-transparent pl-10 pr-3 text-sm text-slate-950 outline-none placeholder:text-slate-400 focus:bg-white dark:text-white dark:placeholder:text-slate-500 dark:focus:bg-white/5 {compact ? 'h-10' : 'h-11'} "
/>
</div>
<button
type="submit"
class=" group inline-flex shrink-0 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-5 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 disabled:cursor-wait disabled:opacity-70 dark:focus-visible:ring-offset-slate-900 {compact ? 'h-10' : 'h-11'} "
disabled="{status === 'loading'}"
>
<span class="icon-[lucide--loader-circle] h-4 w-4 animate-spin" aria-hidden="true" class:hidden="status !== 'loading'" ></span>
<span class:hidden="status === 'loading'">
{buttonLabel}
</span>
<span class:hidden="status !== 'loading'">
Subscribing
</span>
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" class:hidden="status === 'loading'" ></span>
</button>
</div>
</form>
<p
class="mt-3 text-xs leading-5 text-slate-500 dark:text-slate-400"
class:hidden="!showPrivacy"
class:text-center="centered && !compact"
>
No spam. Unsubscribe at any time.
<a
href="{privacyHref}"
class="font-semibold text-indigo-600 transition hover:text-indigo-500 dark:text-indigo-400 dark:hover:text-indigo-300"
class:hidden="privacyLabel === ''"
>
{privacyLabel}
</a>
</p>
<div
role="status"
aria-live="polite"
class="mt-3 rounded-xl px-3 py-2.5 text-sm font-medium"
class:hidden="message === ''"
class:bg-emerald-50="status === 'success'"
class:text-emerald-700="status === 'success'"
class:ring-1="status === 'success'"
class:ring-inset="status === 'success'"
class:ring-emerald-200="status === 'success'"
class:dark:bg-emerald-500/10="status === 'success'"
class:dark:text-emerald-300="status === 'success'"
class:dark:ring-emerald-500/20="status === 'success'"
class:bg-rose-50="status === 'error'"
class:text-rose-700="status === 'error'"
class:ring-1="status === 'error'"
class:ring-inset="status === 'error'"
class:ring-rose-200="status === 'error'"
class:dark:bg-rose-500/10="status === 'error'"
class:dark:text-rose-300="status === 'error'"
class:dark:ring-rose-500/20="status === 'error'"
>
{message}
</div>
</div>
</div>
</div>
</div>
</section>
}
}
+172
View File
@@ -0,0 +1,172 @@
component PageHeader {
props {
class: string = ""
eyebrow = ""
title = ""
description = ""
primaryLabel = ""
primaryHref = ""
secondaryLabel = ""
secondaryHref = ""
icon = "sparkles"
centered = false
compact = false
showBreadcrumbs = false
breadcrumbParent = ""
breadcrumbParentHref = ""
breadcrumbCurrent = ""
highlights = []
}
view {
<section
aria-labelledby="public-page-title"
class="relative overflow-hidden border-b border-slate-200 bg-white dark:border-white/10 dark:bg-slate-950 {class}"
>
<!-- Background decoration -->
<div
aria-hidden="true"
class="pointer-events-none absolute inset-0 overflow-hidden"
>
<div class="absolute -right-40 -top-48 h-96 w-96 rounded-full bg-indigo-300/20 blur-3xl dark:bg-indigo-500/10" ></div>
<div class="absolute -left-40 top-20 h-80 w-80 rounded-full bg-violet-300/15 blur-3xl dark:bg-violet-500/5" ></div>
<div class="absolute left-1/2 top-0 h-px w-2/3 -translate-x-1/2 bg-linear-to-r from-transparent via-indigo-300/60 to-transparent dark:via-indigo-500/20" ></div>
</div>
<div class="relative w-full px-5 sm:px-8 lg:px-10 xl:px-12">
<!-- Breadcrumbs -->
<div
class="border-b border-slate-200/80 py-3 dark:border-white/10"
class:hidden="!showBreadcrumbs"
>
<Breadcrumbs
centered="{centered}"
compact="{false}"
items="{[ ...(breadcrumbParent !== '' ? [{ label: breadcrumbParent, href: breadcrumbParentHref, current: false }] : []), ...(breadcrumbCurrent !== '' ? [{ label: breadcrumbCurrent, href: '', current: true }] : []) ]}"
/>
</div>
<!-- Header body -->
<div
class=" {compact ? 'py-9 sm:py-10 lg:py-12' : 'py-12 sm:py-14 lg:py-16' } {centered ? 'mx-auto flex max-w-5xl flex-col items-center text-center' : 'grid gap-8 lg:grid-cols-[minmax(0,1fr)_auto] lg:items-start lg:gap-12' } "
>
<!-- Content -->
<div class=" min-w-0 {centered ? 'w-full max-w-4xl' : 'max-w-4xl'} " >
<!-- Eyebrow -->
<div
class="flex"
class:hidden="eyebrow === ''"
class:justify-center="centered"
class:justify-start="!centered"
>
<span
class="inline-flex items-center gap-2 rounded-full border border-indigo-200 bg-indigo-50 px-3 py-1.5 text-xs font-semibold text-indigo-700 shadow-sm dark:border-indigo-500/20 dark:bg-indigo-500/10 dark:text-indigo-300"
>
<span class="grid h-5 w-5 place-items-center rounded-full bg-indigo-100 dark:bg-indigo-500/15" >
<span class="icon-[lucide--{icon}] h-3 w-3" aria-hidden="true" ></span>
</span>
{eyebrow}
</span>
</div>
<!-- Title -->
<h1
id="public-page-title"
class=" font-bold tracking-tight text-slate-950 dark:text-white {eyebrow !== '' ? 'mt-6' : ''} {compact ? 'text-3xl leading-tight sm:text-4xl lg:text-5xl' : 'text-4xl leading-[1.08] sm:text-5xl lg:text-[3.25rem]' } "
>
{title}
</h1>
<!-- Description -->
<p
class=" mt-5 text-base text-slate-600 dark:text-slate-400 {compact ? 'leading-7' : 'leading-8 sm:text-lg'} {centered ? 'mx-auto max-w-3xl' : 'max-w-2xl'} "
class:hidden="description === ''"
>
{description}
</p>
</div>
<!-- Single dynamic action group -->
<div
class=" flex flex-col gap-3 sm:flex-row {centered ? 'mt-8 justify-center' : 'lg:pt-1' } "
class:hidden="primaryLabel === '' && secondaryLabel === ''"
>
<!-- Secondary action -->
<a
href="{secondaryHref}"
class="h-12 items-center justify-center rounded-xl border border-slate-200 bg-white px-5 text-sm font-semibold text-slate-700 shadow-sm transition hover:-translate-y-0.5 hover:border-indigo-300 hover:bg-slate-50 hover:text-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-200 dark:hover:border-indigo-500/30 dark:hover:bg-white/10 dark:hover:text-indigo-300"
class:hidden="secondaryLabel === ''"
class:inline-flex="secondaryLabel !== ''"
>
{secondaryLabel}
</a>
<!-- Primary action -->
<a
href="{primaryHref}"
class="group h-12 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-5 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-950"
class:hidden="primaryLabel === ''"
class:inline-flex="primaryLabel !== ''"
>
{primaryLabel}
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
</div>
</div>
<!-- Dynamic highlight strip -->
<div
class="border-t border-slate-200 py-5 dark:border-white/10"
class:hidden="compact || highlights.length === 0"
class:block="!compact && highlights.length > 0"
>
<div class="flex gap-y-5">
{#each highlights as highlight}
<div
class="w-full min-w-0 pr-6"
class:w-full="highlights.length === 1"
class:sm:w-1/2="highlights.length >= 2"
class:lg:w-1/2="highlights.length === 2"
class:lg:w-1/3="highlights.length === 3"
class:lg:w-1/4="highlights.length >= 4"
>
<div
class="flex min-w-0 items-center gap-3"
class:justify-center="centered"
>
<span class="grid h-10 w-10 shrink-0 place-items-center rounded-xl {highlight.iconBoxClass}" >
<span class="{highlight.iconClass} h-5 w-5" aria-hidden="true" ></span>
</span>
<div class="min-w-0">
<p class="text-sm font-semibold text-slate-950 dark:text-white">
{highlight.title}
</p>
<p
class="mt-0.5 text-xs leading-5 text-slate-500 dark:text-slate-400"
class:hidden="highlight.description === ''"
>
{highlight.description}
</p>
</div>
</div>
</div>
{/each}
</div>
</div>
</div>
</section>
}
}
+185 -10
View File
@@ -1,12 +1,187 @@
component Pagination {
props {
class = ""
page = 1
totalPages = 1
previousHref = "#"
nextHref = "#"
}
view {
<nav aria-label="Pagination" class="flex items-center justify-between gap-4 {class}"><a href="{previousHref}" aria-disabled="{page <= 1}" class="inline-flex min-h-11 items-center rounded-xl border border-slate-300 px-4 text-sm font-semibold aria-disabled:pointer-events-none aria-disabled:opacity-50 dark:border-slate-700">← Previous</a><span class="text-sm text-slate-500">Page {page} of {totalPages}</span><a href="{nextHref}" aria-disabled="{page >= totalPages}" class="inline-flex min-h-11 items-center rounded-xl border border-slate-300 px-4 text-sm font-semibold aria-disabled:pointer-events-none aria-disabled:opacity-50 dark:border-slate-700">Next →</a></nav>
}
props {
class: string = ""
currentPage = 1
totalPages = 1
previousHref = ""
nextHref = ""
pages = []
showNumbers = true
showSummary = true
totalItems = 0
pageSize = 10
compact = false
centered = false
}
view {
<nav
aria-label="Pagination"
class="w-full {class}"
class:hidden="totalPages <= 1"
>
<!-- Result summary -->
<div
class="mb-4 text-sm text-slate-500 dark:text-slate-400"
class:hidden="!showSummary || totalItems <= 0"
class:text-center="centered"
>
Showing
<span class="font-semibold text-slate-950 dark:text-white">
{(currentPage - 1) * pageSize + 1}
</span>
to
<span class="font-semibold text-slate-950 dark:text-white">
{currentPage * pageSize > totalItems ? totalItems : currentPage * pageSize}
</span>
of
<span class="font-semibold text-slate-950 dark:text-white">
{totalItems}
</span>
results
</div>
<div
class="flex flex-col gap-4 sm:flex-row sm:items-center"
class:sm:justify-center="centered"
class:sm:justify-between="!centered"
>
<!-- Previous -->
<a
href="{previousHref}"
aria-label="Go to previous page"
class="group h-11 items-center justify-center gap-2 rounded-xl border border-slate-200 bg-white px-4 text-sm font-semibold text-slate-700 shadow-sm transition hover:-translate-y-0.5 hover:border-indigo-300 hover:bg-slate-50 hover:text-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-200 dark:hover:border-indigo-500/30 dark:hover:bg-white/10 dark:hover:text-indigo-300"
class:hidden="previousHref === ''"
class:inline-flex="previousHref !== ''"
>
<span class="icon-[lucide--arrow-left] h-4 w-4 transition-transform group-hover:-translate-x-0.5" aria-hidden="true" ></span>
<span class:hidden="compact">
Previous
</span>
</a>
<span
aria-disabled="true"
class="h-11 cursor-not-allowed items-center justify-center gap-2 rounded-xl border border-slate-200 bg-slate-50 px-4 text-sm font-semibold text-slate-400 dark:border-white/10 dark:bg-white/[0.03] dark:text-slate-600"
class:hidden="previousHref !== ''"
class:inline-flex="previousHref === ''"
>
<span class="icon-[lucide--arrow-left] h-4 w-4" aria-hidden="true" ></span>
<span class:hidden="compact">
Previous
</span>
</span>
<!-- Numbered pages -->
<div
class="flex flex-wrap items-center justify-center gap-1.5"
class:hidden="!showNumbers"
>
{#each pages as page}
<!-- Ellipsis -->
<span
class="grid h-10 w-10 place-items-center text-sm font-semibold text-slate-400"
class:hidden="!page.ellipsis"
>
</span>
<!-- Page link -->
<a
href="{page.href}"
aria-label="Go to page {page.number}"
class="h-10 min-w-10 items-center justify-center rounded-xl px-3 text-sm font-semibold transition focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500"
class:hidden="page.ellipsis || page.current"
class:inline-flex="!page.ellipsis && !page.current"
class:border="!page.current"
class:border-slate-200="!page.current"
class:bg-white="!page.current"
class:text-slate-600="!page.current"
class:hover:border-indigo-300="!page.current"
class:hover:bg-indigo-50="!page.current"
class:hover:text-indigo-700="!page.current"
class:dark:border-white/10="!page.current"
class:dark:bg-white/5="!page.current"
class:dark:text-slate-300="!page.current"
class:dark:hover:border-indigo-500/30="!page.current"
class:dark:hover:bg-indigo-500/10="!page.current"
class:dark:hover:text-indigo-300="!page.current"
>
{page.number}
</a>
<!-- Current page -->
<span
aria-current="page"
aria-label="Current page, page {page.number}"
class="h-10 min-w-10 items-center justify-center rounded-xl bg-indigo-600 px-3 text-sm font-bold text-white shadow-lg shadow-indigo-600/20"
class:hidden="page.ellipsis || !page.current"
class:inline-flex="!page.ellipsis && page.current"
>
{page.number}
</span>
{/each}
</div>
<!-- Mobile page counter -->
<div
class="flex items-center justify-center rounded-xl border border-slate-200 bg-slate-50 px-4 py-2.5 text-sm font-medium text-slate-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-400"
class:hidden="showNumbers"
>
Page
<span class="mx-1 font-bold text-slate-950 dark:text-white">
{currentPage}
</span>
of
<span class="ml-1 font-bold text-slate-950 dark:text-white">
{totalPages}
</span>
</div>
<!-- Next -->
<a
href="{nextHref}"
aria-label="Go to next page"
class="group h-11 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-4 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-950"
class:hidden="nextHref === ''"
class:inline-flex="nextHref !== ''"
>
<span class:hidden="compact">
Next
</span>
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-0.5" aria-hidden="true" ></span>
</a>
<span
aria-disabled="true"
class="h-11 cursor-not-allowed items-center justify-center gap-2 rounded-xl border border-slate-200 bg-slate-50 px-4 text-sm font-semibold text-slate-400 dark:border-white/10 dark:bg-white/[0.03] dark:text-slate-600"
class:hidden="nextHref !== ''"
class:inline-flex="nextHref === ''"
>
<span class:hidden="compact">
Next
</span>
<span class="icon-[lucide--arrow-right] h-4 w-4" aria-hidden="true" ></span>
</span>
</div>
</nav>
}
}
+332
View File
@@ -0,0 +1,332 @@
component ProductMegaMenu {
props {
class: string = ""
}
view {
<div
class="border-t border-slate-200 bg-white shadow-2xl shadow-slate-950/10 dark:border-white/10 dark:bg-slate-950 dark:shadow-black/30 {class}"
>
<div class="mx-auto max-w-7xl px-6 py-8 lg:px-10">
<div class="grid gap-8 lg:grid-cols-[minmax(0,1.65fr)_minmax(18rem,0.7fr)]">
<!-- Main product links -->
<div>
<div class="mb-5 flex items-end justify-between gap-6">
<div>
<p class="text-xs font-semibold uppercase tracking-[0.16em] text-indigo-600 dark:text-indigo-400">
WrNexus platform
</p>
<h2 class="mt-2 text-lg font-bold tracking-tight text-slate-950 dark:text-white">
Everything you need to manage identity
</h2>
</div>
<a
href="/product"
class="group hidden items-center gap-2 text-sm font-semibold text-indigo-600 transition hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300 xl:inline-flex"
>
View all features
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
</div>
<div class="grid gap-2 md:grid-cols-2">
<!-- Platform -->
<a
href="/platform"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-indigo-200 hover:bg-indigo-50 dark:hover:border-indigo-500/20 dark:hover:bg-indigo-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-indigo-100 text-indigo-700 transition group-hover:bg-indigo-600 group-hover:text-white dark:bg-indigo-500/15 dark:text-indigo-300 dark:group-hover:bg-indigo-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--layers-3] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Platform
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-indigo-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-indigo-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Unified identity infrastructure for every application.
</span>
</span>
</a>
<!-- Authentication -->
<a
href="/authentication"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-violet-200 hover:bg-violet-50 dark:hover:border-violet-500/20 dark:hover:bg-violet-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-violet-100 text-violet-700 transition group-hover:bg-violet-600 group-hover:text-white dark:bg-violet-500/15 dark:text-violet-300 dark:group-hover:bg-violet-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--fingerprint] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Authentication
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-violet-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-violet-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Passwords, passkeys, social login, MFA, and SSO.
</span>
</span>
</a>
<!-- User management -->
<a
href="/user-management"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-cyan-200 hover:bg-cyan-50 dark:hover:border-cyan-500/20 dark:hover:bg-cyan-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-cyan-100 text-cyan-700 transition group-hover:bg-cyan-600 group-hover:text-white dark:bg-cyan-500/15 dark:text-cyan-300 dark:group-hover:bg-cyan-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--users-round] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
User management
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-cyan-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-cyan-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Profiles, sessions, invitations, and user lifecycle.
</span>
</span>
</a>
<!-- Organizations -->
<a
href="/organizations"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-emerald-200 hover:bg-emerald-50 dark:hover:border-emerald-500/20 dark:hover:bg-emerald-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-emerald-100 text-emerald-700 transition group-hover:bg-emerald-600 group-hover:text-white dark:bg-emerald-500/15 dark:text-emerald-300 dark:group-hover:bg-emerald-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--building-2] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Organizations
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-emerald-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-emerald-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Multi-tenant workspaces, teams, domains, and membership.
</span>
</span>
</a>
<!-- Authorization -->
<a
href="/authorization"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-amber-200 hover:bg-amber-50 dark:hover:border-amber-500/20 dark:hover:bg-amber-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-amber-100 text-amber-700 transition group-hover:bg-amber-600 group-hover:text-white dark:bg-amber-500/15 dark:text-amber-300 dark:group-hover:bg-amber-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--key-square] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Authorization
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-amber-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-amber-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Roles, permissions, policies, and protected resources.
</span>
</span>
</a>
<!-- Security -->
<a
href="/security"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-rose-200 hover:bg-rose-50 dark:hover:border-rose-500/20 dark:hover:bg-rose-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-rose-100 text-rose-700 transition group-hover:bg-rose-600 group-hover:text-white dark:bg-rose-500/15 dark:text-rose-300 dark:group-hover:bg-rose-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--shield-check] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Security
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-rose-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-rose-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Session controls, recovery, audit logs, and threat protection.
</span>
</span>
</a>
</div>
<a
href="/product"
class="mt-4 flex items-center justify-center gap-2 rounded-xl border border-indigo-200 bg-indigo-50 px-4 py-3 text-sm font-semibold text-indigo-700 transition hover:border-indigo-300 hover:bg-indigo-100 dark:border-indigo-500/20 dark:bg-indigo-500/10 dark:text-indigo-300 dark:hover:border-indigo-500/30 dark:hover:bg-indigo-500/15 xl:hidden"
>
View all product features
<span class="icon-[lucide--arrow-right] h-4 w-4" aria-hidden="true" ></span>
</a>
</div>
<!-- Featured panel -->
<aside
class="relative overflow-hidden rounded-3xl border border-indigo-200 bg-linear-to-br from-indigo-50 via-white to-violet-50 p-6 dark:border-indigo-500/20 dark:from-indigo-500/10 dark:via-slate-900 dark:to-violet-500/10"
>
<div aria-hidden="true" class="absolute -right-16 -top-16 h-40 w-40 rounded-full bg-indigo-300/30 blur-3xl dark:bg-indigo-500/10" ></div>
<div aria-hidden="true" class="absolute -bottom-20 -left-16 h-40 w-40 rounded-full bg-violet-300/30 blur-3xl dark:bg-violet-500/10" ></div>
<div class="relative">
<span
class="inline-flex items-center gap-2 rounded-full border border-indigo-200 bg-white/80 px-3 py-1.5 text-xs font-semibold text-indigo-700 backdrop-blur-sm dark:border-indigo-500/20 dark:bg-white/5 dark:text-indigo-300"
>
<span class="icon-[lucide--sparkles] h-3.5 w-3.5" aria-hidden="true" ></span>
Featured
</span>
<div
class="mt-5 grid h-12 w-12 place-items-center rounded-2xl bg-indigo-600 text-white shadow-lg shadow-indigo-600/20"
>
<span class="icon-[lucide--shield-user] h-6 w-6" aria-hidden="true" ></span>
</div>
<h3 class="mt-5 text-xl font-bold tracking-tight text-slate-950 dark:text-white">
Identity built for modern SaaS
</h3>
<p class="mt-3 text-sm leading-6 text-slate-600 dark:text-slate-400">
Add secure authentication, organizations, permissions, and user
management without rebuilding identity infrastructure.
</p>
<ul class="mt-5 space-y-3">
<li class="flex items-center gap-3 text-sm text-slate-700 dark:text-slate-300">
<span
class="grid h-6 w-6 shrink-0 place-items-center rounded-full bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300"
>
<span class="icon-[lucide--check] h-3.5 w-3.5" aria-hidden="true" ></span>
</span>
Passkeys and multifactor authentication
</li>
<li class="flex items-center gap-3 text-sm text-slate-700 dark:text-slate-300">
<span
class="grid h-6 w-6 shrink-0 place-items-center rounded-full bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300"
>
<span class="icon-[lucide--check] h-3.5 w-3.5" aria-hidden="true" ></span>
</span>
Organizations and workspace isolation
</li>
<li class="flex items-center gap-3 text-sm text-slate-700 dark:text-slate-300">
<span
class="grid h-6 w-6 shrink-0 place-items-center rounded-full bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300"
>
<span class="icon-[lucide--check] h-3.5 w-3.5" aria-hidden="true" ></span>
</span>
APIs and SDKs for every stack
</li>
</ul>
<a
href="/solutions/saas"
class="group mt-6 flex h-11 w-full items-center justify-center gap-2 rounded-xl bg-indigo-600 px-4 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900"
>
Explore SaaS identity
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
<div class="mt-4 flex items-center justify-center gap-2 text-xs text-slate-500 dark:text-slate-400">
<span class="icon-[lucide--code-2] h-4 w-4 text-indigo-600 dark:text-indigo-400" aria-hidden="true" ></span>
Developer-first and API-ready
</div>
</div>
</aside>
</div>
<!-- Bottom quick links -->
<div
class="mt-7 flex flex-wrap items-center justify-between gap-4 border-t border-slate-200 pt-5 dark:border-white/10"
>
<div class="flex flex-wrap items-center gap-x-6 gap-y-3">
<a
href="/integrations"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--plug] h-4 w-4" aria-hidden="true" ></span>
Integrations
</a>
<a
href="/enterprise-sso"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--building-2] h-4 w-4" aria-hidden="true" ></span>
Enterprise SSO
</a>
<a
href="/audit-logs"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--scroll-text] h-4 w-4" aria-hidden="true" ></span>
Audit logs
</a>
</div>
<a
href="/docs"
class="group inline-flex items-center gap-2 text-sm font-semibold text-indigo-600 transition hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300"
>
Read the documentation
<span class="icon-[lucide--arrow-up-right] h-4 w-4 transition-transform group-hover:translate-x-0.5 group-hover:-translate-y-0.5" aria-hidden="true" ></span>
</a>
</div>
</div>
</div>
}
}
+606 -9
View File
@@ -1,11 +1,608 @@
component PublicFooter {
props {
class = ""
brand = "WRNexusJS"
description = "SSR-first, Bun-native full-stack framework."
copyright = "© 2026 WorkRoot Workspace"
}
view {
<footer class="border-t border-slate-200 bg-slate-50 dark:border-slate-800 dark:bg-slate-950 {class}"><div class="mx-auto max-w-7xl px-4 py-14 sm:px-6 lg:px-8"><div class="grid gap-10 lg:grid-cols-[1.2fr_2fr]"><div><div class="text-xl font-black">{brand}</div><p class="mt-3 max-w-sm text-sm leading-6 text-slate-500">{description}</p></div><div class="grid grid-cols-2 gap-8 sm:grid-cols-4"><slot /></div></div><div class="mt-12 flex flex-col gap-4 border-t border-slate-200 pt-6 text-sm text-slate-500 sm:flex-row sm:items-center sm:justify-between dark:border-slate-800"><span>{copyright}</span><button data-wire-theme-toggle type="button" class="min-h-10 rounded-xl border border-slate-300 px-3 dark:border-slate-700">Toggle theme</button></div></div></footer>
}
props {
class: string = ""
}
state email = ""
state newsletterSubmitted = false
view {
<footer
class="relative overflow-hidden border-t border-slate-200 bg-slate-50 dark:border-white/10 dark:bg-slate-950 {class}"
>
<!-- Decorative background -->
<div
aria-hidden="true"
class="pointer-events-none absolute inset-x-0 top-0 h-96 overflow-hidden"
>
<div class="absolute -left-32 -top-48 h-96 w-96 rounded-full bg-indigo-300/20 blur-3xl dark:bg-indigo-500/10"></div>
<div class="absolute -right-32 -top-48 h-96 w-96 rounded-full bg-violet-300/20 blur-3xl dark:bg-violet-500/10"></div>
</div>
<div class="relative w-full px-5 py-12 sm:px-8 lg:px-10 lg:py-16 xl:px-12">
<!-- Newsletter and CTA -->
<section
aria-labelledby="footer-newsletter-title"
class="relative overflow-hidden rounded-3xl border border-indigo-200 bg-linear-to-br from-indigo-50 via-white to-violet-50 p-6 shadow-xl shadow-indigo-950/5 dark:border-indigo-500/20 dark:from-indigo-500/10 dark:via-slate-900 dark:to-violet-500/10 sm:p-8 lg:p-10"
>
<div aria-hidden="true" class="absolute -right-24 -top-24 h-64 w-64 rounded-full bg-indigo-300/30 blur-3xl dark:bg-indigo-500/10" ></div>
<div aria-hidden="true" class="absolute -bottom-24 -left-24 h-64 w-64 rounded-full bg-violet-300/30 blur-3xl dark:bg-violet-500/10" ></div>
<div class="relative grid items-center gap-8 lg:grid-cols-[minmax(0,1fr)_minmax(22rem,0.75fr)]">
<div>
<span
class="inline-flex items-center gap-2 rounded-full border border-indigo-200 bg-white/70 px-3 py-1.5 text-xs font-semibold text-indigo-700 backdrop-blur-sm dark:border-indigo-500/20 dark:bg-white/5 dark:text-indigo-300"
>
<span class="icon-[lucide--mail-plus] h-3.5 w-3.5" aria-hidden="true" ></span>
WrNexus updates
</span>
<h2
id="footer-newsletter-title"
class="mt-5 max-w-2xl text-2xl font-bold tracking-tight text-slate-950 dark:text-white sm:text-3xl"
>
Identity insights delivered to your inbox
</h2>
<p class="mt-3 max-w-2xl text-sm leading-6 text-slate-600 dark:text-slate-400 sm:text-base">
Get product updates, security guidance, implementation
strategies, and practical identity architecture resources.
</p>
</div>
<div>
<form
method="post"
action="/api/newsletter/subscribe"
@submit="newsletterSubmitted = true"
class="rounded-2xl border border-slate-200 bg-white/80 p-2 shadow-lg shadow-slate-950/5 backdrop-blur-sm dark:border-white/10 dark:bg-white/5"
>
<div class="flex flex-col gap-2 sm:flex-row">
<label
class="sr-only"
for="footer-newsletter-email"
>
Work email address
</label>
<div class="relative min-w-0 flex-1">
<span class="icon-[lucide--mail] pointer-events-none absolute left-4 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400" aria-hidden="true" ></span>
<input
id="footer-newsletter-email"
name="email"
type="email"
autocomplete="email"
required
bind:value="email"
placeholder="Enter your work email"
class="h-12 w-full rounded-xl border border-transparent bg-transparent pl-11 pr-4 text-sm text-slate-950 outline-none transition placeholder:text-slate-400 focus:border-indigo-300 focus:bg-white focus:ring-4 focus:ring-indigo-500/10 dark:text-white dark:focus:border-indigo-500/30 dark:focus:bg-slate-900"
/>
</div>
<button
type="submit"
class="group inline-flex h-12 shrink-0 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-5 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900"
>
Subscribe
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</button>
</div>
</form>
<p class="mt-3 flex items-start gap-2 text-xs leading-5 text-slate-500 dark:text-slate-400">
<span class="icon-[lucide--shield-check] mt-0.5 h-3.5 w-3.5 shrink-0" aria-hidden="true" ></span>
No spam. Unsubscribe at any time. Read our
<a
href="/privacy"
class="font-semibold text-indigo-600 hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300"
>
privacy policy
</a>.
</p>
<div
data-show="newsletterSubmitted"
role="status"
class="mt-3 flex items-center gap-2 rounded-xl border border-emerald-200 bg-emerald-50 px-3 py-2.5 text-sm font-medium text-emerald-800 dark:border-emerald-500/20 dark:bg-emerald-500/10 dark:text-emerald-300"
>
<span class="icon-[lucide--circle-check] h-4 w-4 shrink-0" aria-hidden="true" ></span>
Thanks. Please check your inbox to confirm your subscription.
</div>
</div>
</div>
</section>
<!-- Main footer -->
<div class="mt-12 grid gap-10 lg:grid-cols-[minmax(16rem,1.25fr)_minmax(0,3fr)] lg:gap-16">
<!-- Brand -->
<div>
<a
href="/"
aria-label="WrNexus home"
class="group inline-flex items-center gap-3 rounded-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500"
>
<span
class="grid h-11 w-11 place-items-center rounded-2xl bg-indigo-600 text-white shadow-lg shadow-indigo-600/20 transition group-hover:-translate-y-0.5 group-hover:bg-indigo-500"
>
<span class="icon-[lucide--blocks] h-5 w-5" aria-hidden="true" ></span>
</span>
<span>
<span class="block text-lg font-bold tracking-tight text-slate-950 dark:text-white">
WrNexus
</span>
<span
class="block text-[10px] font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400"
>
Identity Cloud
</span>
</span>
</a>
<p class="mt-5 max-w-sm text-sm leading-6 text-slate-600 dark:text-slate-400">
Secure authentication, user management, organizations,
authorization, and enterprise identity for modern applications.
</p>
<div class="mt-6 flex flex-wrap items-center gap-2">
<a
href="/status"
class="inline-flex items-center gap-2 rounded-xl border border-emerald-200 bg-emerald-50 px-3 py-2 text-xs font-semibold text-emerald-800 transition hover:border-emerald-300 hover:bg-emerald-100 dark:border-emerald-500/20 dark:bg-emerald-500/10 dark:text-emerald-300 dark:hover:border-emerald-500/30"
>
<span class="relative flex h-2 w-2">
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-emerald-400 opacity-60"></span>
<span class="relative inline-flex h-2 w-2 rounded-full bg-emerald-500"></span>
</span>
All systems operational
</a>
</div>
<!-- Social links -->
<div class="mt-6 flex items-center gap-2">
<a
href="https://github.com/wrnexus"
target="_blank"
rel="noopener noreferrer"
aria-label="WrNexus on GitHub"
class="grid h-10 w-10 place-items-center rounded-xl border border-slate-200 bg-white text-slate-500 transition hover:border-indigo-300 hover:text-indigo-700 dark:border-white/10 dark:bg-white/5 dark:text-slate-400 dark:hover:border-indigo-500/30 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--github] h-4.5 w-4.5" aria-hidden="true" ></span>
</a>
<a
href="https://www.linkedin.com/company/wrnexus"
target="_blank"
rel="noopener noreferrer"
aria-label="WrNexus on LinkedIn"
class="grid h-10 w-10 place-items-center rounded-xl border border-slate-200 bg-white text-slate-500 transition hover:border-indigo-300 hover:text-indigo-700 dark:border-white/10 dark:bg-white/5 dark:text-slate-400 dark:hover:border-indigo-500/30 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--linkedin] h-4.5 w-4.5" aria-hidden="true" ></span>
</a>
<a
href="https://x.com/wrnexus"
target="_blank"
rel="noopener noreferrer"
aria-label="WrNexus on X"
class="grid h-10 w-10 place-items-center rounded-xl border border-slate-200 bg-white text-slate-500 transition hover:border-indigo-300 hover:text-indigo-700 dark:border-white/10 dark:bg-white/5 dark:text-slate-400 dark:hover:border-indigo-500/30 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--twitter] h-4.5 w-4.5" aria-hidden="true" ></span>
</a>
<a
href="https://www.youtube.com/@wrnexus"
target="_blank"
rel="noopener noreferrer"
aria-label="WrNexus on YouTube"
class="grid h-10 w-10 place-items-center rounded-xl border border-slate-200 bg-white text-slate-500 transition hover:border-indigo-300 hover:text-indigo-700 dark:border-white/10 dark:bg-white/5 dark:text-slate-400 dark:hover:border-indigo-500/30 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--youtube] h-4.5 w-4.5" aria-hidden="true" ></span>
</a>
</div>
</div>
<!-- Navigation columns -->
<nav
aria-label="Footer navigation"
class="grid grid-cols-2 gap-x-6 gap-y-10 sm:grid-cols-3 xl:grid-cols-5"
>
<!-- Product -->
<div>
<h3 class="text-sm font-bold text-slate-950 dark:text-white">
Product
</h3>
<ul class="mt-4 space-y-3">
<li>
<a
href="/platform"
class="footer-link"
>
Platform
</a>
</li>
<li>
<a
href="/authentication"
class="footer-link"
>
Authentication
</a>
</li>
<li>
<a
href="/user-management"
class="footer-link"
>
User management
</a>
</li>
<li>
<a
href="/organizations"
class="footer-link"
>
Organizations
</a>
</li>
<li>
<a
href="/authorization"
class="footer-link"
>
Authorization
</a>
</li>
<li>
<a
href="/enterprise-sso"
class="footer-link"
>
Enterprise SSO
</a>
</li>
</ul>
</div>
<!-- Solutions -->
<div>
<h3 class="text-sm font-bold text-slate-950 dark:text-white">
Solutions
</h3>
<ul class="mt-4 space-y-3">
<li>
<a
href="/solutions/startups"
class="footer-link"
>
Startups
</a>
</li>
<li>
<a
href="/solutions/saas"
class="footer-link"
>
SaaS
</a>
</li>
<li>
<a
href="/solutions/enterprise"
class="footer-link"
>
Enterprise
</a>
</li>
<li>
<a
href="/solutions/developers"
class="footer-link"
>
Developers
</a>
</li>
<li>
<a
href="/solutions/agencies"
class="footer-link"
>
Agencies
</a>
</li>
<li>
<a
href="/solutions/b2b"
class="footer-link"
>
B2B
</a>
</li>
</ul>
</div>
<!-- Developers -->
<div>
<h3 class="text-sm font-bold text-slate-950 dark:text-white">
Developers
</h3>
<ul class="mt-4 space-y-3">
<li>
<a
href="/docs"
class="footer-link"
>
Documentation
</a>
</li>
<li>
<a
href="/docs/api"
class="footer-link"
>
API reference
</a>
</li>
<li>
<a
href="/sdks"
class="footer-link"
>
SDKs
</a>
</li>
<li>
<a
href="/examples"
class="footer-link"
>
Examples
</a>
</li>
<li>
<a
href="/docs/webhooks"
class="footer-link"
>
Webhooks
</a>
</li>
<li>
<a
href="/changelog"
class="footer-link"
>
Changelog
</a>
</li>
</ul>
</div>
<!-- Resources -->
<div>
<h3 class="text-sm font-bold text-slate-950 dark:text-white">
Resources
</h3>
<ul class="mt-4 space-y-3">
<li>
<a
href="/blog"
class="footer-link"
>
Blog
</a>
</li>
<li>
<a
href="/guides"
class="footer-link"
>
Guides
</a>
</li>
<li>
<a
href="/customers"
class="footer-link"
>
Customers
</a>
</li>
<li>
<a
href="/help"
class="footer-link"
>
Help center
</a>
</li>
<li>
<a
href="/security"
class="footer-link"
>
Security
</a>
</li>
<li>
<a
href="/trust"
class="footer-link"
>
Trust center
</a>
</li>
</ul>
</div>
<!-- Company -->
<div>
<h3 class="text-sm font-bold text-slate-950 dark:text-white">
Company
</h3>
<ul class="mt-4 space-y-3">
<li>
<a
href="/about"
class="footer-link"
>
About
</a>
</li>
<li>
<a
href="/careers"
class="footer-link"
>
Careers
</a>
</li>
<li>
<a
href="/partners"
class="footer-link"
>
Partners
</a>
</li>
<li>
<a
href="/contact"
class="footer-link"
>
Contact
</a>
</li>
<li>
<a
href="/pricing"
class="footer-link"
>
Pricing
</a>
</li>
<li>
<a
href="/status"
class="footer-link"
>
Status
</a>
</li>
</ul>
</div>
</nav>
</div>
<!-- Bottom bar -->
<div
class="mt-12 flex flex-col gap-5 border-t border-slate-200 pt-6 dark:border-white/10 lg:flex-row lg:items-center lg:justify-between"
>
<div
class="flex flex-col gap-2 text-xs text-slate-500 dark:text-slate-400 sm:flex-row sm:items-center sm:gap-4"
>
<p>
© 2026 WrNexus. All rights reserved.
</p>
<span class="hidden h-1 w-1 rounded-full bg-slate-300 dark:bg-slate-700 sm:block"></span>
<p>
Built by WorkRoot Workspace.
</p>
</div>
<div class="flex flex-wrap items-center gap-x-5 gap-y-3">
<a
href="/privacy"
class="footer-bottom-link"
>
Privacy
</a>
<a
href="/terms"
class="footer-bottom-link"
>
Terms
</a>
<a
href="/cookies"
class="footer-bottom-link"
>
Cookies
</a>
<a
href="/legal"
class="footer-bottom-link"
>
Legal
</a>
<button
type="button"
data-open-cookie-preferences
class="footer-bottom-link"
>
Cookie preferences
</button>
<button
type="button"
data-wire-theme-toggle
class="inline-flex items-center gap-2 rounded-lg text-xs font-semibold text-slate-500 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--moon] h-3.5 w-3.5 dark:hidden" aria-hidden="true" ></span>
<span class="icon-[lucide--sun] hidden h-3.5 w-3.5 dark:block" aria-hidden="true" ></span>
Theme
</button>
</div>
</div>
</div>
</footer>
}
}
+282 -12
View File
@@ -1,14 +1,284 @@
component PublicHeader {
props {
class = ""
brand = "WRNexusJS"
homeHref = "/"
signInHref = "/signin"
ctaHref = "/start"
ctaLabel = "Get started"
}
state mobileOpen = false
view {
<header class="sticky top-0 z-40 border-b border-slate-200/80 bg-white/85 backdrop-blur-xl dark:border-slate-800 dark:bg-slate-950/85 {class}"><div class="mx-auto flex h-16 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8"><a href="{homeHref}" class="text-lg font-black tracking-tight">{brand}</a><nav class="hidden items-center gap-7 md:flex" aria-label="Primary"><slot /></nav><div class="hidden items-center gap-3 md:flex"><a href="{signInHref}" class="text-sm font-semibold">Sign in</a><a href="{ctaHref}" class="inline-flex min-h-10 items-center rounded-xl bg-violet-600 px-4 text-sm font-semibold text-white">{ctaLabel}</a><button data-wire-theme-toggle type="button" aria-label="Toggle theme" class="size-10 rounded-xl hover:bg-slate-100 dark:hover:bg-slate-800">◐</button></div><button type="button" aria-label="Open menu" aria-expanded="{mobileOpen}" @click="mobileOpen = !mobileOpen" class="size-11 rounded-xl md:hidden">☰</button></div><div data-show="mobileOpen" class="border-t border-slate-200 px-4 py-5 md:hidden dark:border-slate-800"><div class="space-y-3"><slot name="mobile" /></div><div class="mt-5 grid grid-cols-2 gap-3"><a href="{signInHref}" class="inline-flex min-h-11 items-center justify-center rounded-xl border border-slate-300 font-semibold">Sign in</a><a href="{ctaHref}" class="inline-flex min-h-11 items-center justify-center rounded-xl bg-violet-600 font-semibold text-white">{ctaLabel}</a></div></div></header>
}
props {
class: string = ""
}
state activeMenu = ""
state mobileMenuOpen = false
state headerScrolled = false
view {
<header
data-public-header
class="relative sticky top-0 z-50 w-full border-b transition-all duration-300 {class}"
class:border-transparent="!headerScrolled"
class:bg-transparent="!headerScrolled"
class:border-slate-200/80="headerScrolled"
class:bg-white/90="headerScrolled"
class:shadow-sm="headerScrolled"
class:backdrop-blur-xl="headerScrolled"
class:dark:border-white/10="headerScrolled"
class:dark:bg-slate-950/90="headerScrolled"
>
<div
class="relative z-[60] flex h-18 w-full items-center justify-between gap-6 px-5 sm:px-8 lg:px-10 xl:px-12"
>
<!-- Brand -->
<a
href="/"
aria-label="WrNexus home"
class="group inline-flex shrink-0 items-center gap-3 rounded-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-950"
>
<span
class="grid h-10 w-10 place-items-center rounded-xl bg-indigo-600 text-white shadow-lg shadow-indigo-600/20 transition duration-200 group-hover:-translate-y-0.5 group-hover:bg-indigo-500 group-hover:shadow-xl"
>
<span class="icon-[lucide--blocks] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="block text-lg font-bold tracking-tight text-slate-950 dark:text-white">
WrNexus
</span>
<span
class="block text-[10px] font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400"
>
Identity Cloud
</span>
</span>
</a>
<!-- Desktop navigation -->
<nav
aria-label="Main navigation"
class="hidden items-center gap-1 lg:flex"
>
<!-- Product -->
<button
type="button"
@click="activeMenu = activeMenu === 'product' ? '' : 'product'"
aria-haspopup="true"
aria-expanded="{activeMenu === 'product'}"
class="inline-flex h-10 items-center gap-1.5 rounded-xl px-3.5 text-sm font-semibold transition"
class:bg-indigo-50="activeMenu === 'product'"
class:text-indigo-700="activeMenu === 'product'"
class:text-slate-700="activeMenu !== 'product'"
class:hover:bg-slate-100="activeMenu !== 'product'"
class:dark:bg-indigo-500/10="activeMenu === 'product'"
class:dark:text-indigo-300="activeMenu === 'product'"
class:dark:text-slate-300="activeMenu !== 'product'"
class:dark:hover:bg-white/5="activeMenu !== 'product'"
>
Product
<span class="icon-[lucide--chevron-down] h-4 w-4 transition-transform duration-200" class:rotate-180="activeMenu === 'product'" aria-hidden="true" ></span>
</button>
<!-- Solutions -->
<button
type="button"
@click="activeMenu = activeMenu === 'solutions' ? '' : 'solutions'"
aria-haspopup="true"
aria-expanded="{activeMenu === 'solutions'}"
class="group inline-flex h-10 items-center gap-1.5 rounded-xl px-3.5 text-sm font-semibold transition"
class:bg-indigo-50="activeMenu === 'solutions'"
class:text-indigo-700="activeMenu === 'solutions'"
class:text-slate-700="activeMenu !== 'solutions'"
class:hover:bg-slate-100="activeMenu !== 'solutions'"
class:hover:text-slate-950="activeMenu !== 'solutions'"
class:dark:bg-indigo-500/10="activeMenu === 'solutions'"
class:dark:text-indigo-300="activeMenu === 'solutions'"
class:dark:text-slate-300="activeMenu !== 'solutions'"
class:dark:hover:bg-white/5="activeMenu !== 'solutions'"
class:dark:hover:text-white="activeMenu !== 'solutions'"
>
Solutions
<span class="icon-[lucide--chevron-down] h-4 w-4 transition-transform duration-200" class:rotate-180="activeMenu === 'solutions'" aria-hidden="true" ></span>
</button>
<!-- Developers -->
<button
type="button"
@click="activeMenu = activeMenu === 'developers' ? '' : 'developers'"
aria-haspopup="true"
aria-expanded="{activeMenu === 'developers'}"
class="group inline-flex h-10 items-center gap-1.5 rounded-xl px-3.5 text-sm font-semibold transition"
class:bg-indigo-50="activeMenu === 'developers'"
class:text-indigo-700="activeMenu === 'developers'"
class:text-slate-700="activeMenu !== 'developers'"
class:hover:bg-slate-100="activeMenu !== 'developers'"
class:hover:text-slate-950="activeMenu !== 'developers'"
class:dark:bg-indigo-500/10="activeMenu === 'developers'"
class:dark:text-indigo-300="activeMenu === 'developers'"
class:dark:text-slate-300="activeMenu !== 'developers'"
class:dark:hover:bg-white/5="activeMenu !== 'developers'"
class:dark:hover:text-white="activeMenu !== 'developers'"
>
Developers
<span class="icon-[lucide--chevron-down] h-4 w-4 transition-transform duration-200" class:rotate-180="activeMenu === 'developers'" aria-hidden="true" ></span>
</button>
<!-- Resources -->
<button
type="button"
@click="activeMenu = activeMenu === 'resources' ? '' : 'resources'"
aria-haspopup="true"
aria-expanded="{activeMenu === 'resources'}"
class="group inline-flex h-10 items-center gap-1.5 rounded-xl px-3.5 text-sm font-semibold transition"
class:bg-indigo-50="activeMenu === 'resources'"
class:text-indigo-700="activeMenu === 'resources'"
class:text-slate-700="activeMenu !== 'resources'"
class:hover:bg-slate-100="activeMenu !== 'resources'"
class:hover:text-slate-950="activeMenu !== 'resources'"
class:dark:bg-indigo-500/10="activeMenu === 'resources'"
class:dark:text-indigo-300="activeMenu === 'resources'"
class:dark:text-slate-300="activeMenu !== 'resources'"
class:dark:hover:bg-white/5="activeMenu !== 'resources'"
class:dark:hover:text-white="activeMenu !== 'resources'"
>
Resources
<span class="icon-[lucide--chevron-down] h-4 w-4 transition-transform duration-200" class:rotate-180="activeMenu === 'resources'" aria-hidden="true" ></span>
</button>
<a
href="/pricing"
class="inline-flex h-10 items-center rounded-xl px-3.5 text-sm font-semibold text-slate-700 transition hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:text-slate-300 dark:hover:bg-white/5 dark:hover:text-white"
>
Pricing
</a>
</nav>
<!-- Desktop actions -->
<div class="hidden items-center gap-2 lg:flex">
<a
href="/status"
class="mr-1 inline-flex items-center gap-2 rounded-xl px-3 py-2 text-xs font-semibold text-slate-500 transition hover:bg-slate-100 hover:text-slate-950 dark:text-slate-400 dark:hover:bg-white/5 dark:hover:text-white"
>
<span class="relative flex h-2 w-2">
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-emerald-400 opacity-60"></span>
<span class="relative inline-flex h-2 w-2 rounded-full bg-emerald-500"></span>
</span>
All systems operational
</a>
<button
type="button"
data-wire-theme-toggle
aria-label="Toggle color theme"
title="Toggle color theme"
class="grid h-10 w-10 place-items-center rounded-xl border border-slate-200 bg-white text-slate-600 shadow-sm transition hover:border-slate-300 hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-300 dark:hover:border-white/20 dark:hover:bg-white/10 dark:hover:text-white"
>
<span class="icon-[lucide--moon] h-4 w-4 dark:hidden" aria-hidden="true" ></span>
<span class="icon-[lucide--sun] hidden h-4 w-4 dark:block" aria-hidden="true" ></span>
<span class="sr-only">
Toggle color theme
</span>
</button>
<a
href="/sign-in"
class="inline-flex h-10 items-center justify-center rounded-xl px-4 text-sm font-semibold text-slate-700 transition hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:text-slate-200 dark:hover:bg-white/5 dark:hover:text-white"
>
Sign in
</a>
<a
href="/sign-up"
class="group inline-flex h-10 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-4 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-950"
>
Start free
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-0.5" aria-hidden="true" ></span>
</a>
</div>
<!-- Mobile actions -->
<div class="flex items-center gap-2 lg:hidden">
<button
type="button"
data-wire-theme-toggle
aria-label="Toggle color theme"
title="Toggle color theme"
class="grid h-10 w-10 place-items-center rounded-xl border border-slate-200 bg-white text-slate-600 shadow-sm transition hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-300 dark:hover:bg-white/10 dark:hover:text-white"
>
<span class="icon-[lucide--moon] h-4 w-4 dark:hidden" aria-hidden="true" ></span>
<span class="icon-[lucide--sun] hidden h-4 w-4 dark:block" aria-hidden="true" ></span>
</button>
<button
type="button"
@click="mobileMenuOpen = !mobileMenuOpen"
aria-label="{mobileMenuOpen ? 'Close navigation menu' : 'Open navigation menu'}"
aria-expanded="{mobileMenuOpen}"
aria-controls="public-mobile-navigation"
class="grid h-10 w-10 place-items-center rounded-xl border border-slate-200 bg-white text-slate-700 shadow-sm transition hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-200 dark:hover:bg-white/10 dark:hover:text-white"
>
<span data-show="!mobileMenuOpen" class="icon-[lucide--menu] h-5 w-5" aria-hidden="true" ></span>
<span data-show="mobileMenuOpen" class="icon-[lucide--x] h-5 w-5" aria-hidden="true" ></span>
<span class="sr-only">
Toggle navigation
</span>
</button>
</div>
</div>
<!-- Outside-click backdrop -->
<button type="button" data-show="activeMenu !== ''" @click="activeMenu = ''" aria-label="Close navigation menu" class="fixed inset-0 z-40 hidden cursor-default bg-transparent lg:block" class:hidden="activeMenu === ''" class:block="activeMenu !== ''" ></button>
<!-- Desktop mega menus -->
<div
class="absolute inset-x-0 top-full z-50 max-lg:hidden"
class:hidden="activeMenu === ''"
class:block="activeMenu !== ''"
>
<div
class:hidden="activeMenu !== 'product'"
class:block="activeMenu === 'product'"
>
<ProductMegaMenu />
</div>
<div
class:hidden="activeMenu !== 'solutions'"
class:block="activeMenu === 'solutions'"
>
<SolutionsMegaMenu />
</div>
<div
class:hidden="activeMenu !== 'developers'"
class:block="activeMenu === 'developers'"
>
<DevelopersMegaMenu />
</div>
<div
class:hidden="activeMenu !== 'resources'"
class:block="activeMenu === 'resources'"
>
<ResourcesMegaMenu />
</div>
</div>
<!-- Mobile navigation -->
<div
id="public-mobile-navigation"
class="lg:hidden"
class:hidden="!mobileMenuOpen"
class:block="mobileMenuOpen"
>
<PublicMobileNavigation />
</div>
</header>
}
}
@@ -0,0 +1,589 @@
component PublicMobileNavigation {
props {
class: string = ""
}
state openSection = ""
view {
<div
class="border-t border-slate-200 bg-white shadow-xl dark:border-white/10 dark:bg-slate-950 lg:hidden {class}"
>
<div class="mx-auto max-h-[calc(100dvh-4.5rem)] max-w-7xl overflow-y-auto px-5 py-5 sm:px-8">
<!-- Mobile status -->
<a
href="/status"
class="mb-5 flex items-center justify-between rounded-2xl border border-emerald-200 bg-emerald-50 px-4 py-3 transition hover:border-emerald-300 hover:bg-emerald-100 dark:border-emerald-500/20 dark:bg-emerald-500/10 dark:hover:border-emerald-500/30 dark:hover:bg-emerald-500/15"
>
<span class="flex items-center gap-3">
<span class="relative flex h-2.5 w-2.5 shrink-0">
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-emerald-400 opacity-60"></span>
<span class="relative inline-flex h-2.5 w-2.5 rounded-full bg-emerald-500"></span>
</span>
<span>
<span class="block text-sm font-semibold text-emerald-900 dark:text-emerald-100">
All systems operational
</span>
<span class="mt-0.5 block text-xs text-emerald-700 dark:text-emerald-300">
View service status
</span>
</span>
</span>
<span class="icon-[lucide--arrow-up-right] h-4 w-4 shrink-0 text-emerald-700 dark:text-emerald-300" aria-hidden="true" ></span>
</a>
<nav
aria-label="Mobile navigation"
class="space-y-2"
>
<!-- Product -->
<div
class="overflow-hidden rounded-2xl border border-slate-200 bg-white dark:border-white/10 dark:bg-white/5"
>
<button
type="button"
@click="openSection = openSection === 'product' ? '' : 'product'"
aria-expanded="{openSection === 'product'}"
aria-controls="mobile-product-menu"
class="flex w-full items-center justify-between gap-4 px-4 py-4 text-left transition hover:bg-slate-50 dark:hover:bg-white/5"
class:bg-indigo-50="openSection === 'product'"
class:dark:bg-indigo-500/10="openSection === 'product'"
>
<span class="flex items-center gap-3">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl transition"
class:bg-indigo-100="openSection === 'product'"
class:text-indigo-700="openSection === 'product'"
class:bg-slate-100="openSection !== 'product'"
class:text-slate-600="openSection !== 'product'"
class:dark:bg-indigo-500/15="openSection === 'product'"
class:dark:text-indigo-300="openSection === 'product'"
class:dark:bg-white/5="openSection !== 'product'"
class:dark:text-slate-300="openSection !== 'product'"
>
<span class="icon-[lucide--boxes] h-5 w-5" aria-hidden="true" ></span>
</span>
<span>
<span
class="block text-sm font-semibold"
class:text-indigo-950="openSection === 'product'"
class:text-slate-950="openSection !== 'product'"
class:dark:text-indigo-100="openSection === 'product'"
class:dark:text-white="openSection !== 'product'"
>
Product
</span>
<span class="mt-0.5 block text-xs text-slate-500 dark:text-slate-400">
Explore the WrNexus platform
</span>
</span>
</span>
<span class="icon-[lucide--chevron-down] h-5 w-5 shrink-0 text-slate-400 transition-transform duration-200" class:rotate-180="openSection === 'product'" aria-hidden="true" ></span>
</button>
<div
id="mobile-product-menu"
data-show="openSection === 'product'"
class="border-t border-slate-200 px-3 py-3 dark:border-white/10"
>
<div class="grid gap-1">
<a
href="/platform"
class="group flex items-start gap-3 rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span
class="grid h-9 w-9 shrink-0 place-items-center rounded-lg bg-indigo-50 text-indigo-700 dark:bg-indigo-500/10 dark:text-indigo-300"
>
<span class="icon-[lucide--layers-3] h-4 w-4" aria-hidden="true" ></span>
</span>
<span>
<span
class="block text-sm font-semibold text-slate-900 group-hover:text-indigo-700 dark:text-white dark:group-hover:text-indigo-300"
>
Platform
</span>
<span class="mt-0.5 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Complete identity infrastructure
</span>
</span>
</a>
<a
href="/authentication"
class="group flex items-start gap-3 rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span
class="grid h-9 w-9 shrink-0 place-items-center rounded-lg bg-violet-50 text-violet-700 dark:bg-violet-500/10 dark:text-violet-300"
>
<span class="icon-[lucide--fingerprint] h-4 w-4" aria-hidden="true" ></span>
</span>
<span>
<span
class="block text-sm font-semibold text-slate-900 group-hover:text-indigo-700 dark:text-white dark:group-hover:text-indigo-300"
>
Authentication
</span>
<span class="mt-0.5 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Passwords, passkeys, social login, and SSO
</span>
</span>
</a>
<a
href="/user-management"
class="group flex items-start gap-3 rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span
class="grid h-9 w-9 shrink-0 place-items-center rounded-lg bg-cyan-50 text-cyan-700 dark:bg-cyan-500/10 dark:text-cyan-300"
>
<span class="icon-[lucide--users-round] h-4 w-4" aria-hidden="true" ></span>
</span>
<span>
<span
class="block text-sm font-semibold text-slate-900 group-hover:text-indigo-700 dark:text-white dark:group-hover:text-indigo-300"
>
User management
</span>
<span class="mt-0.5 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Profiles, sessions, invitations, and lifecycle
</span>
</span>
</a>
<a
href="/organizations"
class="group flex items-start gap-3 rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span
class="grid h-9 w-9 shrink-0 place-items-center rounded-lg bg-emerald-50 text-emerald-700 dark:bg-emerald-500/10 dark:text-emerald-300"
>
<span class="icon-[lucide--building-2] h-4 w-4" aria-hidden="true" ></span>
</span>
<span>
<span
class="block text-sm font-semibold text-slate-900 group-hover:text-indigo-700 dark:text-white dark:group-hover:text-indigo-300"
>
Organizations
</span>
<span class="mt-0.5 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Multi-tenant workspaces and teams
</span>
</span>
</a>
<a
href="/security"
class="group flex items-start gap-3 rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span
class="grid h-9 w-9 shrink-0 place-items-center rounded-lg bg-amber-50 text-amber-700 dark:bg-amber-500/10 dark:text-amber-300"
>
<span class="icon-[lucide--shield-check] h-4 w-4" aria-hidden="true" ></span>
</span>
<span>
<span
class="block text-sm font-semibold text-slate-900 group-hover:text-indigo-700 dark:text-white dark:group-hover:text-indigo-300"
>
Security
</span>
<span class="mt-0.5 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Protect identities and sensitive actions
</span>
</span>
</a>
<a
href="/integrations"
class="group flex items-start gap-3 rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span
class="grid h-9 w-9 shrink-0 place-items-center rounded-lg bg-rose-50 text-rose-700 dark:bg-rose-500/10 dark:text-rose-300"
>
<span class="icon-[lucide--plug] h-4 w-4" aria-hidden="true" ></span>
</span>
<span>
<span
class="block text-sm font-semibold text-slate-900 group-hover:text-indigo-700 dark:text-white dark:group-hover:text-indigo-300"
>
Integrations
</span>
<span class="mt-0.5 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Connect your stack and applications
</span>
</span>
</a>
</div>
<a
href="/product"
class="mt-2 flex items-center justify-center gap-2 rounded-xl bg-indigo-50 px-4 py-3 text-sm font-semibold text-indigo-700 transition hover:bg-indigo-100 dark:bg-indigo-500/10 dark:text-indigo-300 dark:hover:bg-indigo-500/15"
>
View all product features
<span class="icon-[lucide--arrow-right] h-4 w-4" aria-hidden="true" ></span>
</a>
</div>
</div>
<!-- Solutions -->
<div
class="overflow-hidden rounded-2xl border border-slate-200 bg-white dark:border-white/10 dark:bg-white/5"
>
<button
type="button"
@click="openSection = openSection === 'solutions' ? '' : 'solutions'"
aria-expanded="{openSection === 'solutions'}"
aria-controls="mobile-solutions-menu"
class="flex w-full items-center justify-between gap-4 px-4 py-4 text-left transition hover:bg-slate-50 dark:hover:bg-white/5"
class:bg-indigo-50="openSection === 'solutions'"
class:dark:bg-indigo-500/10="openSection === 'solutions'"
>
<span class="flex items-center gap-3">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl transition"
class:bg-indigo-100="openSection === 'solutions'"
class:text-indigo-700="openSection === 'solutions'"
class:bg-slate-100="openSection !== 'solutions'"
class:text-slate-600="openSection !== 'solutions'"
class:dark:bg-indigo-500/15="openSection === 'solutions'"
class:dark:text-indigo-300="openSection === 'solutions'"
class:dark:bg-white/5="openSection !== 'solutions'"
class:dark:text-slate-300="openSection !== 'solutions'"
>
<span class="icon-[lucide--lightbulb] h-5 w-5" aria-hidden="true" ></span>
</span>
<span>
<span class="block text-sm font-semibold text-slate-950 dark:text-white">
Solutions
</span>
<span class="mt-0.5 block text-xs text-slate-500 dark:text-slate-400">
Built for your business
</span>
</span>
</span>
<span class="icon-[lucide--chevron-down] h-5 w-5 shrink-0 text-slate-400 transition-transform duration-200" class:rotate-180="openSection === 'solutions'" aria-hidden="true" ></span>
</button>
<div
id="mobile-solutions-menu"
data-show="openSection === 'solutions'"
class="border-t border-slate-200 px-3 py-3 dark:border-white/10"
>
<div class="grid grid-cols-2 gap-2">
<a
href="/solutions/startups"
class="rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span class="icon-[lucide--rocket] h-5 w-5 text-indigo-600 dark:text-indigo-400"></span>
<span class="mt-2 block text-sm font-semibold text-slate-900 dark:text-white">
Startups
</span>
</a>
<a
href="/solutions/saas"
class="rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span class="icon-[lucide--cloud] h-5 w-5 text-cyan-600 dark:text-cyan-400"></span>
<span class="mt-2 block text-sm font-semibold text-slate-900 dark:text-white">
SaaS
</span>
</a>
<a
href="/solutions/enterprise"
class="rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span class="icon-[lucide--building] h-5 w-5 text-violet-600 dark:text-violet-400"></span>
<span class="mt-2 block text-sm font-semibold text-slate-900 dark:text-white">
Enterprise
</span>
</a>
<a
href="/solutions/developers"
class="rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span class="icon-[lucide--code-2] h-5 w-5 text-emerald-600 dark:text-emerald-400"></span>
<span class="mt-2 block text-sm font-semibold text-slate-900 dark:text-white">
Developers
</span>
</a>
<a
href="/solutions/agencies"
class="rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span class="icon-[lucide--briefcase-business] h-5 w-5 text-amber-600 dark:text-amber-400"></span>
<span class="mt-2 block text-sm font-semibold text-slate-900 dark:text-white">
Agencies
</span>
</a>
<a
href="/solutions/b2b"
class="rounded-xl px-3 py-3 transition hover:bg-slate-100 dark:hover:bg-white/5"
>
<span class="icon-[lucide--handshake] h-5 w-5 text-rose-600 dark:text-rose-400"></span>
<span class="mt-2 block text-sm font-semibold text-slate-900 dark:text-white">
B2B
</span>
</a>
</div>
</div>
</div>
<!-- Developers -->
<div
class="overflow-hidden rounded-2xl border border-slate-200 bg-white dark:border-white/10 dark:bg-white/5"
>
<button
type="button"
@click="openSection = openSection === 'developers' ? '' : 'developers'"
aria-expanded="{openSection === 'developers'}"
aria-controls="mobile-developers-menu"
class="flex w-full items-center justify-between gap-4 px-4 py-4 text-left transition hover:bg-slate-50 dark:hover:bg-white/5"
class:bg-indigo-50="openSection === 'developers'"
class:dark:bg-indigo-500/10="openSection === 'developers'"
>
<span class="flex items-center gap-3">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl transition"
class:bg-indigo-100="openSection === 'developers'"
class:text-indigo-700="openSection === 'developers'"
class:bg-slate-100="openSection !== 'developers'"
class:text-slate-600="openSection !== 'developers'"
class:dark:bg-indigo-500/15="openSection === 'developers'"
class:dark:text-indigo-300="openSection === 'developers'"
class:dark:bg-white/5="openSection !== 'developers'"
class:dark:text-slate-300="openSection !== 'developers'"
>
<span class="icon-[lucide--terminal-square] h-5 w-5" aria-hidden="true" ></span>
</span>
<span>
<span class="block text-sm font-semibold text-slate-950 dark:text-white">
Developers
</span>
<span class="mt-0.5 block text-xs text-slate-500 dark:text-slate-400">
Build with WrNexus
</span>
</span>
</span>
<span class="icon-[lucide--chevron-down] h-5 w-5 shrink-0 text-slate-400 transition-transform duration-200" class:rotate-180="openSection === 'developers'" aria-hidden="true" ></span>
</button>
<div
id="mobile-developers-menu"
data-show="openSection === 'developers'"
class="border-t border-slate-200 px-3 py-3 dark:border-white/10"
>
<div class="grid gap-1">
<a
href="/docs"
class="flex items-center gap-3 rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 hover:text-indigo-700 dark:text-slate-200 dark:hover:bg-white/5 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--book-open] h-5 w-5 text-indigo-600 dark:text-indigo-400"></span>
Documentation
</a>
<a
href="/docs/api"
class="flex items-center gap-3 rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 hover:text-indigo-700 dark:text-slate-200 dark:hover:bg-white/5 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--braces] h-5 w-5 text-violet-600 dark:text-violet-400"></span>
API reference
</a>
<a
href="/sdks"
class="flex items-center gap-3 rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 hover:text-indigo-700 dark:text-slate-200 dark:hover:bg-white/5 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--package] h-5 w-5 text-cyan-600 dark:text-cyan-400"></span>
SDKs
</a>
<a
href="/examples"
class="flex items-center gap-3 rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 hover:text-indigo-700 dark:text-slate-200 dark:hover:bg-white/5 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--flask-conical] h-5 w-5 text-emerald-600 dark:text-emerald-400"></span>
Examples
</a>
<a
href="/changelog"
class="flex items-center gap-3 rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 hover:text-indigo-700 dark:text-slate-200 dark:hover:bg-white/5 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--history] h-5 w-5 text-amber-600 dark:text-amber-400"></span>
Changelog
</a>
</div>
</div>
</div>
<!-- Resources -->
<div
class="overflow-hidden rounded-2xl border border-slate-200 bg-white dark:border-white/10 dark:bg-white/5"
>
<button
type="button"
@click="openSection = openSection === 'resources' ? '' : 'resources'"
aria-expanded="{openSection === 'resources'}"
aria-controls="mobile-resources-menu"
class="flex w-full items-center justify-between gap-4 px-4 py-4 text-left transition hover:bg-slate-50 dark:hover:bg-white/5"
class:bg-indigo-50="openSection === 'resources'"
class:dark:bg-indigo-500/10="openSection === 'resources'"
>
<span class="flex items-center gap-3">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl transition"
class:bg-indigo-100="openSection === 'resources'"
class:text-indigo-700="openSection === 'resources'"
class:bg-slate-100="openSection !== 'resources'"
class:text-slate-600="openSection !== 'resources'"
class:dark:bg-indigo-500/15="openSection === 'resources'"
class:dark:text-indigo-300="openSection === 'resources'"
class:dark:bg-white/5="openSection !== 'resources'"
class:dark:text-slate-300="openSection !== 'resources'"
>
<span class="icon-[lucide--library] h-5 w-5" aria-hidden="true" ></span>
</span>
<span>
<span class="block text-sm font-semibold text-slate-950 dark:text-white">
Resources
</span>
<span class="mt-0.5 block text-xs text-slate-500 dark:text-slate-400">
Learn and get support
</span>
</span>
</span>
<span class="icon-[lucide--chevron-down] h-5 w-5 shrink-0 text-slate-400 transition-transform duration-200" class:rotate-180="openSection === 'resources'" aria-hidden="true" ></span>
</button>
<div
id="mobile-resources-menu"
data-show="openSection === 'resources'"
class="border-t border-slate-200 px-3 py-3 dark:border-white/10"
>
<div class="grid grid-cols-2 gap-2">
<a
href="/blog"
class="rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-white/5"
>
Blog
</a>
<a
href="/customers"
class="rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-white/5"
>
Customer stories
</a>
<a
href="/guides"
class="rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-white/5"
>
Guides
</a>
<a
href="/help"
class="rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-white/5"
>
Help center
</a>
<a
href="/security"
class="rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-white/5"
>
Security
</a>
<a
href="/trust"
class="rounded-xl px-3 py-3 text-sm font-semibold text-slate-800 transition hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-white/5"
>
Trust center
</a>
</div>
</div>
</div>
<!-- Direct links -->
<a
href="/pricing"
class="flex items-center justify-between rounded-2xl border border-slate-200 bg-white px-4 py-4 text-sm font-semibold text-slate-950 transition hover:border-indigo-300 hover:bg-indigo-50 dark:border-white/10 dark:bg-white/5 dark:text-white dark:hover:border-indigo-500 dark:hover:bg-indigo-500/10"
>
<span class="flex items-center gap-3">
<span
class="grid h-10 w-10 place-items-center rounded-xl bg-slate-100 text-slate-600 dark:bg-white/5 dark:text-slate-300"
>
<span class="icon-[lucide--badge-dollar-sign] h-5 w-5" aria-hidden="true" ></span>
</span>
Pricing
</span>
<span class="icon-[lucide--chevron-right] h-5 w-5 text-slate-400" aria-hidden="true" ></span>
</a>
</nav>
<!-- Mobile CTAs -->
<div class="mt-5 grid gap-3 border-t border-slate-200 pt-5 dark:border-white/10 sm:grid-cols-2">
<a
href="/sign-in"
class="flex h-12 items-center justify-center gap-2 rounded-xl border border-slate-200 bg-white px-5 text-sm font-semibold text-slate-700 shadow-sm transition hover:border-indigo-300 hover:bg-slate-50 hover:text-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-200 dark:hover:border-indigo-500 dark:hover:bg-white/10 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--log-in] h-4 w-4" aria-hidden="true" ></span>
Sign in
</a>
<a
href="/sign-up"
class="group flex h-12 items-center justify-center gap-2 rounded-xl bg-indigo-600 px-5 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500"
>
Start free
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
</div>
<p class="mt-5 text-center text-xs leading-5 text-slate-400 dark:text-slate-500">
Secure identity infrastructure for modern applications.
</p>
</div>
</div>
}
}
+125
View File
@@ -0,0 +1,125 @@
component PublicSearch {
props {
class: string = ""
query = ""
placeholder = "Search..."
label = "Search"
action = ""
method = "get"
name = "q"
buttonLabel = ""
clearLabel = "Clear search"
size = "default"
centered = false
fullWidth = false
showShortcut = false
shortcutLabel = "⌘ K"
suggestions = []
}
state searchValue = query
state focused = false
view {
<div
class="w-full {class}"
class:max-w-2xl="!fullWidth"
class:mx-auto="centered"
>
<form
action="{action}"
method="{method}"
role="search"
class="w-full"
>
<label
for="public-search-input"
class="sr-only"
>
{label}
</label>
<div
class=" group relative flex w-full items-center rounded-2xl border bg-white shadow-sm transition-all duration-200 dark:bg-white/5 {focused ? 'border-indigo-400 ring-4 ring-indigo-500/10 dark:border-indigo-500/50 dark:ring-indigo-500/10' : 'border-slate-200 hover:border-slate-300 dark:border-white/10 dark:hover:border-white/20' } {size === 'compact' ? 'min-h-11' : size === 'large' ? 'min-h-14' : 'min-h-12' } "
>
<!-- Search icon -->
<span class=" pointer-events-none absolute left-4 text-slate-400 transition group-focus-within:text-indigo-600 dark:text-slate-500 dark:group-focus-within:text-indigo-400 {size === 'large' ? 'h-5 w-5' : 'h-4.5 w-4.5'} icon-[lucide--search] " aria-hidden="true" ></span>
<!-- Input -->
<input
id="public-search-input"
type="search"
name="{name}"
value="{searchValue}"
placeholder="{placeholder}"
autocomplete="off"
@input="searchValue = event.target.value"
@focus="focused = true"
@blur="focused = false"
class=" min-w-0 flex-1 bg-transparent text-slate-950 outline-none placeholder:text-slate-400 dark:text-white dark:placeholder:text-slate-500 {size === 'compact' ? 'h-11 pl-11 pr-3 text-sm' : size === 'large' ? 'h-14 pl-12 pr-4 text-base' : 'h-12 pl-11 pr-4 text-sm' } "
/>
<!-- Keyboard shortcut -->
<span
class="mr-3 hidden shrink-0 items-center rounded-md border border-slate-200 bg-slate-50 px-2 py-1 text-[11px] font-semibold text-slate-400 dark:border-white/10 dark:bg-white/5 dark:text-slate-500 sm:inline-flex"
class:hidden="!showShortcut || searchValue !== ''"
>
{shortcutLabel}
</span>
<!-- Clear button -->
<button
type="button"
@click="searchValue = ''"
aria-label="{clearLabel}"
class="mr-2 grid h-8 w-8 shrink-0 place-items-center rounded-lg text-slate-400 transition hover:bg-slate-100 hover:text-slate-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:text-slate-500 dark:hover:bg-white/10 dark:hover:text-slate-200"
class:hidden="searchValue === ''"
>
<span class="icon-[lucide--x] h-4 w-4" aria-hidden="true" ></span>
</button>
<!-- Submit button -->
<button
type="submit"
class=" mr-1.5 shrink-0 items-center justify-center gap-2 rounded-xl bg-indigo-600 font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900 {size === 'compact' ? 'h-9 px-3 text-xs' : size === 'large' ? 'h-11 px-5 text-sm' : 'h-10 px-4 text-sm' } "
class:hidden="buttonLabel === ''"
class:inline-flex="buttonLabel !== ''"
>
{buttonLabel}
<span class="icon-[lucide--arrow-right] h-4 w-4" aria-hidden="true" ></span>
</button>
</div>
</form>
<!-- Suggested searches -->
<div
class="mt-4 flex flex-wrap items-center gap-2"
class:hidden="suggestions.length === 0"
class:justify-center="centered"
class:justify-start="!centered"
>
<span class="mr-1 text-xs font-medium text-slate-500 dark:text-slate-400">
Popular:
</span>
{#each suggestions.slice(0, 5) as suggestion}
<a
href="{suggestion.href}"
class="inline-flex items-center gap-1.5 rounded-full border border-slate-200 bg-white px-3 py-1.5 text-xs font-semibold text-slate-600 shadow-sm transition hover:-translate-y-0.5 hover:border-indigo-300 hover:bg-indigo-50 hover:text-indigo-700 dark:border-white/10 dark:bg-white/5 dark:text-slate-300 dark:hover:border-indigo-500/30 dark:hover:bg-indigo-500/10 dark:hover:text-indigo-300"
>
<span class="{suggestion.iconClass} h-3.5 w-3.5" aria-hidden="true" class:hidden="suggestion.iconClass === ''" ></span>
{suggestion.label}
</a>
{/each}
</div>
</div>
}
}
+363 -8
View File
@@ -1,10 +1,365 @@
component ResourcesMegaMenu {
props {
title = ""
description = ""
open = false
closeLabel = "Close"
class = ""
}
view { <div data-show="open" class="wire-overlay wire-catalog-overlay {class}"><section role="dialog" aria-modal="true" aria-label="{title}" class="wire-dialog"><header><h2 data-show="title" class="wire-dialog__title">{title}</h2><p data-show="description" class="wire-dialog__description">{description}</p></header><slot /><button type="button" class="wire-btn wire-btn--ghost">{closeLabel}</button></section></div> }
props {
class: string = ""
}
view {
<div
class="border-t border-slate-200 bg-white shadow-2xl shadow-slate-950/10 dark:border-white/10 dark:bg-slate-950 dark:shadow-black/30 {class}"
>
<div class="mx-auto max-w-7xl px-6 py-8 lg:px-10">
<div class="grid gap-8 lg:grid-cols-[minmax(0,1.55fr)_minmax(18rem,0.8fr)]">
<!-- Resource links -->
<section>
<div class="mb-5 flex items-end justify-between gap-6">
<div>
<p class="text-xs font-semibold uppercase tracking-[0.16em] text-indigo-600 dark:text-indigo-400">
Resources
</p>
<h2 class="mt-2 text-lg font-bold tracking-tight text-slate-950 dark:text-white">
Learn, build, and get support
</h2>
<p class="mt-1 text-sm text-slate-500 dark:text-slate-400">
Explore practical guides, customer stories, product updates,
and security resources.
</p>
</div>
<a
href="/resources"
class="group hidden items-center gap-2 text-sm font-semibold text-indigo-600 transition hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300 xl:inline-flex"
>
View all resources
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
</div>
<div class="grid gap-2 md:grid-cols-2">
<!-- Blog -->
<a
href="/blog"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-indigo-200 hover:bg-indigo-50 dark:hover:border-indigo-500/20 dark:hover:bg-indigo-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-indigo-100 text-indigo-700 transition group-hover:bg-indigo-600 group-hover:text-white dark:bg-indigo-500/15 dark:text-indigo-300 dark:group-hover:bg-indigo-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--newspaper] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Blog
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-indigo-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-indigo-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Product updates, engineering insights, identity trends, and
company news.
</span>
</span>
</a>
<!-- Customer stories -->
<a
href="/customers"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-violet-200 hover:bg-violet-50 dark:hover:border-violet-500/20 dark:hover:bg-violet-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-violet-100 text-violet-700 transition group-hover:bg-violet-600 group-hover:text-white dark:bg-violet-500/15 dark:text-violet-300 dark:group-hover:bg-violet-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--users-round] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Customer stories
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-violet-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-violet-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
See how teams use WrNexus to secure and scale their
applications.
</span>
</span>
</a>
<!-- Guides -->
<a
href="/guides"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-cyan-200 hover:bg-cyan-50 dark:hover:border-cyan-500/20 dark:hover:bg-cyan-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-cyan-100 text-cyan-700 transition group-hover:bg-cyan-600 group-hover:text-white dark:bg-cyan-500/15 dark:text-cyan-300 dark:group-hover:bg-cyan-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--book-marked] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Guides
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-cyan-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-cyan-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Practical identity, security, SaaS, and architecture
walkthroughs.
</span>
</span>
</a>
<!-- Help center -->
<a
href="/help"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-emerald-200 hover:bg-emerald-50 dark:hover:border-emerald-500/20 dark:hover:bg-emerald-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-emerald-100 text-emerald-700 transition group-hover:bg-emerald-600 group-hover:text-white dark:bg-emerald-500/15 dark:text-emerald-300 dark:group-hover:bg-emerald-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--circle-help] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Help center
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-emerald-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-emerald-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Find answers for account, workspace, billing, and security
questions.
</span>
</span>
</a>
<!-- Security -->
<a
href="/security"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-amber-200 hover:bg-amber-50 dark:hover:border-amber-500/20 dark:hover:bg-amber-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-amber-100 text-amber-700 transition group-hover:bg-amber-600 group-hover:text-white dark:bg-amber-500/15 dark:text-amber-300 dark:group-hover:bg-amber-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--shield-check] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Security
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-amber-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-amber-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Learn how WrNexus protects accounts, sessions, and
organizations.
</span>
</span>
</a>
<!-- Trust center -->
<a
href="/trust"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-rose-200 hover:bg-rose-50 dark:hover:border-rose-500/20 dark:hover:bg-rose-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-rose-100 text-rose-700 transition group-hover:bg-rose-600 group-hover:text-white dark:bg-rose-500/15 dark:text-rose-300 dark:group-hover:bg-rose-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--badge-check] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Trust center
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-rose-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-rose-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Review compliance, privacy, infrastructure, and operational
controls.
</span>
</span>
</a>
</div>
<a
href="/resources"
class="mt-4 flex items-center justify-center gap-2 rounded-xl border border-indigo-200 bg-indigo-50 px-4 py-3 text-sm font-semibold text-indigo-700 transition hover:border-indigo-300 hover:bg-indigo-100 dark:border-indigo-500/20 dark:bg-indigo-500/10 dark:text-indigo-300 dark:hover:border-indigo-500/30 dark:hover:bg-indigo-500/15 xl:hidden"
>
View all resources
<span class="icon-[lucide--arrow-right] h-4 w-4" aria-hidden="true" ></span>
</a>
</section>
<!-- Featured resource -->
<aside
class="relative overflow-hidden rounded-3xl border border-indigo-200 bg-linear-to-br from-indigo-50 via-white to-cyan-50 p-6 dark:border-indigo-500/20 dark:from-indigo-500/10 dark:via-slate-900 dark:to-cyan-500/10"
>
<div aria-hidden="true" class="absolute -right-16 -top-16 h-40 w-40 rounded-full bg-indigo-300/30 blur-3xl dark:bg-indigo-500/10" ></div>
<div aria-hidden="true" class="absolute -bottom-20 -left-16 h-40 w-40 rounded-full bg-cyan-300/30 blur-3xl dark:bg-cyan-500/10" ></div>
<div class="relative">
<span
class="inline-flex items-center gap-2 rounded-full border border-indigo-200 bg-white/80 px-3 py-1.5 text-xs font-semibold text-indigo-700 backdrop-blur-sm dark:border-indigo-500/20 dark:bg-white/5 dark:text-indigo-300"
>
<span class="icon-[lucide--sparkles] h-3.5 w-3.5" aria-hidden="true" ></span>
Featured guide
</span>
<div
class="mt-5 grid h-12 w-12 place-items-center rounded-2xl bg-indigo-600 text-white shadow-lg shadow-indigo-600/20"
>
<span class="icon-[lucide--book-open-check] h-6 w-6" aria-hidden="true" ></span>
</div>
<h3 class="mt-5 text-xl font-bold tracking-tight text-slate-950 dark:text-white">
The modern SaaS identity checklist
</h3>
<p class="mt-3 text-sm leading-6 text-slate-600 dark:text-slate-400">
A practical checklist covering authentication, organizations,
permissions, MFA, recovery, sessions, and enterprise SSO.
</p>
<div class="mt-5 space-y-3">
<div class="flex items-center gap-3">
<span
class="grid h-7 w-7 shrink-0 place-items-center rounded-full bg-indigo-100 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300"
>
<span class="icon-[lucide--check] h-4 w-4" aria-hidden="true" ></span>
</span>
<span class="text-sm text-slate-700 dark:text-slate-300">
Secure authentication foundations
</span>
</div>
<div class="flex items-center gap-3">
<span
class="grid h-7 w-7 shrink-0 place-items-center rounded-full bg-indigo-100 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300"
>
<span class="icon-[lucide--check] h-4 w-4" aria-hidden="true" ></span>
</span>
<span class="text-sm text-slate-700 dark:text-slate-300">
Multi-tenant organization design
</span>
</div>
<div class="flex items-center gap-3">
<span
class="grid h-7 w-7 shrink-0 place-items-center rounded-full bg-indigo-100 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300"
>
<span class="icon-[lucide--check] h-4 w-4" aria-hidden="true" ></span>
</span>
<span class="text-sm text-slate-700 dark:text-slate-300">
Recovery and account protection
</span>
</div>
</div>
<a
href="/guides/saas-identity-checklist"
class="group mt-6 flex h-11 w-full items-center justify-center gap-2 rounded-xl bg-indigo-600 px-4 text-sm font-bold text-white shadow-lg shadow-indigo-600/20 transition hover:-translate-y-0.5 hover:bg-indigo-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900"
>
Read the guide
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
<div class="mt-4 flex items-center justify-center gap-4 text-xs text-slate-500 dark:text-slate-400">
<span class="inline-flex items-center gap-1.5">
<span class="icon-[lucide--clock-3] h-4 w-4" aria-hidden="true" ></span>
12 min read
</span>
<span class="inline-flex items-center gap-1.5">
<span class="icon-[lucide--download] h-4 w-4" aria-hidden="true" ></span>
PDF available
</span>
</div>
</div>
</aside>
</div>
<!-- Bottom resource links -->
<div
class="mt-7 flex flex-wrap items-center justify-between gap-4 border-t border-slate-200 pt-5 dark:border-white/10"
>
<div class="flex flex-wrap items-center gap-x-6 gap-y-3">
<a
href="/webinars"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--presentation] h-4 w-4" aria-hidden="true" ></span>
Webinars
</a>
<a
href="/events"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--calendar-days] h-4 w-4" aria-hidden="true" ></span>
Events
</a>
<a
href="/reports"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--file-chart-column] h-4 w-4" aria-hidden="true" ></span>
Reports
</a>
<a
href="/newsletter"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--mail-plus] h-4 w-4" aria-hidden="true" ></span>
Newsletter
</a>
</div>
<a
href="/contact"
class="group inline-flex items-center gap-2 text-sm font-semibold text-indigo-600 transition hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300"
>
Contact our team
<span class="icon-[lucide--arrow-up-right] h-4 w-4 transition-transform group-hover:translate-x-0.5 group-hover:-translate-y-0.5" aria-hidden="true" ></span>
</a>
</div>
</div>
</div>
}
}
+143
View File
@@ -0,0 +1,143 @@
component SocialShare {
props {
class: string = ""
title = "Share this page"
description = ""
url = ""
shareText = ""
centered = false
compact = false
showTitle = true
showCopy = true
networks = []
}
state copied = false
view {
<aside
aria-labelledby="social-share-title"
class="w-full {class}"
>
<div
class=" relative overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-white/10 dark:bg-white/[0.03] "
>
<!-- Subtle background -->
<div
aria-hidden="true"
class="pointer-events-none absolute inset-0 overflow-hidden"
>
<div class="absolute -right-16 -top-20 h-40 w-40 rounded-full bg-indigo-200/25 blur-3xl dark:bg-indigo-500/10" ></div>
<div class="absolute inset-x-0 top-0 h-px bg-linear-to-r from-transparent via-indigo-300/60 to-transparent dark:via-indigo-500/20" ></div>
</div>
<div
class="relative"
class:p-4="compact"
class:sm:p-5="compact"
class:p-5="!compact"
class:sm:p-6="!compact"
>
<div
class="gap-4"
class:flex="centered"
class:flex-col="centered"
class:items-center="centered"
class:text-center="centered"
class:flex="!centered"
class:flex-col="!centered"
class:sm:flex-row="!centered"
class:sm:items-center="!centered"
class:sm:justify-between="!centered"
>
<!-- Heading -->
<div
class="min-w-0"
class:hidden="!showTitle"
>
<h2
id="social-share-title"
class="font-bold tracking-tight text-slate-950 dark:text-white"
class:text-sm="compact"
class:text-base="!compact"
>
{title}
</h2>
<p
class="mt-1 text-sm leading-6 text-slate-500 dark:text-slate-400"
class:hidden="description === '' || compact"
>
{description}
</p>
</div>
<!-- Share buttons -->
<div
class="flex flex-wrap items-center gap-2"
class:justify-center="centered"
class:justify-start="!centered"
>
{#each networks.slice(0, 5) as network}
<a
href="{network.href}"
target="_blank"
rel="noopener noreferrer"
aria-label="Share on {network.label}"
title="Share on {network.label}"
class=" group inline-flex items-center justify-center gap-2 rounded-xl border border-slate-200 bg-white text-sm font-semibold text-slate-600 shadow-sm transition hover:-translate-y-0.5 hover:border-indigo-300 hover:bg-indigo-50 hover:text-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-300 dark:hover:border-indigo-500/30 dark:hover:bg-indigo-500/10 dark:hover:text-indigo-300 "
class:h-9="compact"
class:w-9="compact"
class:h-10="!compact"
class:px-3="!compact"
>
<span class="{network.iconClass} h-4 w-4" aria-hidden="true" ></span>
<span class:hidden="compact">
{network.label}
</span>
</a>
{/each}
<!-- Copy link -->
<button
type="button"
@click=" navigator.clipboard.writeText(url); copied = true; "
aria-label="{copied ? 'Link copied' : 'Copy page link'}"
class=" group inline-flex items-center justify-center gap-2 rounded-xl border border-slate-200 bg-white text-sm font-semibold text-slate-600 shadow-sm transition hover:-translate-y-0.5 hover:border-indigo-300 hover:bg-indigo-50 hover:text-indigo-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:border-white/10 dark:bg-white/5 dark:text-slate-300 dark:hover:border-indigo-500/30 dark:hover:bg-indigo-500/10 dark:hover:text-indigo-300 "
class:hidden="!showCopy"
class:h-9="compact"
class:w-9="compact"
class:h-10="!compact"
class:px-3="!compact"
>
<span class="icon-[lucide--copy] h-4 w-4" class:hidden="copied" aria-hidden="true" ></span>
<span class="icon-[lucide--check] h-4 w-4" class:hidden="!copied" aria-hidden="true" ></span>
<span class:hidden="compact">
{copied ? 'Copied' : 'Copy link'}
</span>
</button>
</div>
</div>
<!-- Copy feedback -->
<div
role="status"
aria-live="polite"
class="mt-3 rounded-xl bg-emerald-50 px-3 py-2 text-sm font-medium text-emerald-700 ring-1 ring-inset ring-emerald-200 dark:bg-emerald-500/10 dark:text-emerald-300 dark:ring-emerald-500/20"
class:hidden="!copied"
class:text-center="centered"
>
Page link copied to your clipboard.
</div>
</div>
</div>
</aside>
}
}
+366 -8
View File
@@ -1,10 +1,368 @@
component SolutionsMegaMenu {
props {
title = ""
description = ""
open = false
closeLabel = "Close"
class = ""
}
view { <div data-show="open" class="wire-overlay wire-catalog-overlay {class}"><section role="dialog" aria-modal="true" aria-label="{title}" class="wire-dialog"><header><h2 data-show="title" class="wire-dialog__title">{title}</h2><p data-show="description" class="wire-dialog__description">{description}</p></header><slot /><button type="button" class="wire-btn wire-btn--ghost">{closeLabel}</button></section></div> }
props {
class: string = ""
}
view {
<div
class="border-t border-slate-200 bg-white shadow-2xl shadow-slate-950/10 dark:border-white/10 dark:bg-slate-950 dark:shadow-black/30 {class}"
>
<div class="mx-auto max-w-7xl px-6 py-8 lg:px-10">
<div class="grid gap-8 lg:grid-cols-[minmax(0,1.6fr)_minmax(18rem,0.75fr)]">
<!-- Solution links -->
<section>
<div class="mb-5 flex items-end justify-between gap-6">
<div>
<p class="text-xs font-semibold uppercase tracking-[0.16em] text-indigo-600 dark:text-indigo-400">
Solutions
</p>
<h2 class="mt-2 text-lg font-bold tracking-tight text-slate-950 dark:text-white">
Identity infrastructure for every stage of growth
</h2>
<p class="mt-1 text-sm text-slate-500 dark:text-slate-400">
Choose a solution designed around your product, team, and customers.
</p>
</div>
<a
href="/solutions"
class="group hidden items-center gap-2 text-sm font-semibold text-indigo-600 transition hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300 xl:inline-flex"
>
View all solutions
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
</div>
<div class="grid gap-2 md:grid-cols-2">
<!-- Startups -->
<a
href="/solutions/startups"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-indigo-200 hover:bg-indigo-50 dark:hover:border-indigo-500/20 dark:hover:bg-indigo-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-indigo-100 text-indigo-700 transition group-hover:bg-indigo-600 group-hover:text-white dark:bg-indigo-500/15 dark:text-indigo-300 dark:group-hover:bg-indigo-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--rocket] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Startups
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-indigo-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-indigo-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Launch secure authentication quickly without slowing product development.
</span>
</span>
</a>
<!-- SaaS -->
<a
href="/solutions/saas"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-cyan-200 hover:bg-cyan-50 dark:hover:border-cyan-500/20 dark:hover:bg-cyan-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-cyan-100 text-cyan-700 transition group-hover:bg-cyan-600 group-hover:text-white dark:bg-cyan-500/15 dark:text-cyan-300 dark:group-hover:bg-cyan-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--cloud] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
SaaS platforms
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-cyan-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-cyan-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Add users, organizations, roles, SSO, and secure account recovery.
</span>
</span>
</a>
<!-- Enterprise -->
<a
href="/solutions/enterprise"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-violet-200 hover:bg-violet-50 dark:hover:border-violet-500/20 dark:hover:bg-violet-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-violet-100 text-violet-700 transition group-hover:bg-violet-600 group-hover:text-white dark:bg-violet-500/15 dark:text-violet-300 dark:group-hover:bg-violet-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--building-2] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Enterprise
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-violet-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-violet-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Support complex organizations, governance, auditability, and scale.
</span>
</span>
</a>
<!-- Developers -->
<a
href="/solutions/developers"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-emerald-200 hover:bg-emerald-50 dark:hover:border-emerald-500/20 dark:hover:bg-emerald-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-emerald-100 text-emerald-700 transition group-hover:bg-emerald-600 group-hover:text-white dark:bg-emerald-500/15 dark:text-emerald-300 dark:group-hover:bg-emerald-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--code-2] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Developers
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-emerald-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-emerald-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
APIs, SDKs, webhooks, templates, and developer-friendly workflows.
</span>
</span>
</a>
<!-- Agencies -->
<a
href="/solutions/agencies"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-amber-200 hover:bg-amber-50 dark:hover:border-amber-500/20 dark:hover:bg-amber-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-amber-100 text-amber-700 transition group-hover:bg-amber-600 group-hover:text-white dark:bg-amber-500/15 dark:text-amber-300 dark:group-hover:bg-amber-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--briefcase-business] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
Agencies
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-amber-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-amber-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Manage identities across customer projects and branded experiences.
</span>
</span>
</a>
<!-- B2B -->
<a
href="/solutions/b2b"
class="group flex items-start gap-4 rounded-2xl border border-transparent p-4 transition hover:border-rose-200 hover:bg-rose-50 dark:hover:border-rose-500/20 dark:hover:bg-rose-500/10"
>
<span
class="grid h-11 w-11 shrink-0 place-items-center rounded-xl bg-rose-100 text-rose-700 transition group-hover:bg-rose-600 group-hover:text-white dark:bg-rose-500/15 dark:text-rose-300 dark:group-hover:bg-rose-500 dark:group-hover:text-white"
>
<span class="icon-[lucide--handshake] h-5 w-5" aria-hidden="true" ></span>
</span>
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="text-sm font-semibold text-slate-950 dark:text-white">
B2B products
</span>
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5 text-slate-300 opacity-0 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-rose-600 group-hover:opacity-100 dark:text-slate-600 dark:group-hover:text-rose-300" aria-hidden="true" ></span>
</span>
<span class="mt-1 block text-xs leading-5 text-slate-500 dark:text-slate-400">
Secure customer organizations, team access, and enterprise connections.
</span>
</span>
</a>
</div>
<a
href="/solutions"
class="mt-4 flex items-center justify-center gap-2 rounded-xl border border-indigo-200 bg-indigo-50 px-4 py-3 text-sm font-semibold text-indigo-700 transition hover:border-indigo-300 hover:bg-indigo-100 dark:border-indigo-500/20 dark:bg-indigo-500/10 dark:text-indigo-300 dark:hover:border-indigo-500/30 dark:hover:bg-indigo-500/15 xl:hidden"
>
View all solutions
<span class="icon-[lucide--arrow-right] h-4 w-4" aria-hidden="true" ></span>
</a>
</section>
<!-- Featured solution -->
<aside
class="relative overflow-hidden rounded-3xl border border-violet-200 bg-linear-to-br from-violet-50 via-white to-indigo-50 p-6 dark:border-violet-500/20 dark:from-violet-500/10 dark:via-slate-900 dark:to-indigo-500/10"
>
<div aria-hidden="true" class="absolute -right-16 -top-16 h-40 w-40 rounded-full bg-violet-300/30 blur-3xl dark:bg-violet-500/10" ></div>
<div aria-hidden="true" class="absolute -bottom-20 -left-16 h-40 w-40 rounded-full bg-indigo-300/30 blur-3xl dark:bg-indigo-500/10" ></div>
<div class="relative">
<span
class="inline-flex items-center gap-2 rounded-full border border-violet-200 bg-white/80 px-3 py-1.5 text-xs font-semibold text-violet-700 backdrop-blur-sm dark:border-violet-500/20 dark:bg-white/5 dark:text-violet-300"
>
<span class="icon-[lucide--wand-sparkles] h-3.5 w-3.5" aria-hidden="true" ></span>
Recommended for SaaS
</span>
<div
class="mt-5 grid h-12 w-12 place-items-center rounded-2xl bg-violet-600 text-white shadow-lg shadow-violet-600/20"
>
<span class="icon-[lucide--cloud-cog] h-6 w-6" aria-hidden="true" ></span>
</div>
<h3 class="mt-5 text-xl font-bold tracking-tight text-slate-950 dark:text-white">
Launch enterprise-ready identity
</h3>
<p class="mt-3 text-sm leading-6 text-slate-600 dark:text-slate-400">
Start with simple authentication and add organizations,
permissions, enterprise SSO, and governance as your product grows.
</p>
<div class="mt-5 grid gap-3">
<div
class="flex items-center gap-3 rounded-xl border border-violet-200 bg-white/70 px-3 py-3 dark:border-violet-500/20 dark:bg-white/5"
>
<span
class="grid h-8 w-8 shrink-0 place-items-center rounded-lg bg-violet-100 text-violet-700 dark:bg-violet-500/15 dark:text-violet-300"
>
<span class="icon-[lucide--timer-reset] h-4 w-4" aria-hidden="true" ></span>
</span>
<span>
<span class="block text-sm font-semibold text-slate-900 dark:text-white">
Faster launch
</span>
<span class="block text-xs text-slate-500 dark:text-slate-400">
Start with secure defaults
</span>
</span>
</div>
<div
class="flex items-center gap-3 rounded-xl border border-violet-200 bg-white/70 px-3 py-3 dark:border-violet-500/20 dark:bg-white/5"
>
<span
class="grid h-8 w-8 shrink-0 place-items-center rounded-lg bg-indigo-100 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300"
>
<span class="icon-[lucide--expand] h-4 w-4" aria-hidden="true" ></span>
</span>
<span>
<span class="block text-sm font-semibold text-slate-900 dark:text-white">
Built to scale
</span>
<span class="block text-xs text-slate-500 dark:text-slate-400">
Add advanced controls later
</span>
</span>
</div>
<div
class="flex items-center gap-3 rounded-xl border border-violet-200 bg-white/70 px-3 py-3 dark:border-violet-500/20 dark:bg-white/5"
>
<span
class="grid h-8 w-8 shrink-0 place-items-center rounded-lg bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300"
>
<span class="icon-[lucide--shield-check] h-4 w-4" aria-hidden="true" ></span>
</span>
<span>
<span class="block text-sm font-semibold text-slate-900 dark:text-white">
Secure by design
</span>
<span class="block text-xs text-slate-500 dark:text-slate-400">
Protect every account flow
</span>
</span>
</div>
</div>
<a
href="/solutions/saas"
class="group mt-6 flex h-11 w-full items-center justify-center gap-2 rounded-xl bg-violet-600 px-4 text-sm font-bold text-white shadow-lg shadow-violet-600/20 transition hover:-translate-y-0.5 hover:bg-violet-500 hover:shadow-xl focus:outline-none focus-visible:ring-2 focus-visible:ring-violet-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-slate-900"
>
Explore the SaaS solution
<span class="icon-[lucide--arrow-right] h-4 w-4 transition-transform group-hover:translate-x-1" aria-hidden="true" ></span>
</a>
</div>
</aside>
</div>
<!-- Bottom use-case links -->
<div
class="mt-7 flex flex-wrap items-center justify-between gap-4 border-t border-slate-200 pt-5 dark:border-white/10"
>
<div class="flex flex-wrap items-center gap-x-6 gap-y-3">
<a
href="/solutions/customer-identity"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--contact-round] h-4 w-4" aria-hidden="true" ></span>
Customer identity
</a>
<a
href="/solutions/workforce-identity"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--id-card] h-4 w-4" aria-hidden="true" ></span>
Workforce identity
</a>
<a
href="/solutions/marketplaces"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--store] h-4 w-4" aria-hidden="true" ></span>
Marketplaces
</a>
<a
href="/solutions/multi-tenant"
class="inline-flex items-center gap-2 text-sm font-semibold text-slate-600 transition hover:text-indigo-700 dark:text-slate-400 dark:hover:text-indigo-300"
>
<span class="icon-[lucide--network] h-4 w-4" aria-hidden="true" ></span>
Multi-tenant apps
</a>
</div>
<a
href="/customers"
class="group inline-flex items-center gap-2 text-sm font-semibold text-indigo-600 transition hover:text-indigo-800 dark:text-indigo-400 dark:hover:text-indigo-300"
>
See customer stories
<span class="icon-[lucide--arrow-up-right] h-4 w-4 transition-transform group-hover:translate-x-0.5 group-hover:-translate-y-0.5" aria-hidden="true" ></span>
</a>
</div>
</div>
</div>
}
}
+122
View File
@@ -0,0 +1,122 @@
component StatusBanner {
props {
class: string = ""
type = "info"
title = ""
description = ""
actionLabel = ""
actionHref = ""
dismissible = true
compact = false
details = []
}
state visible = true
view {
<aside
role="{type === 'error' ? 'alert' : 'status'}"
aria-live="{type === 'error' ? 'assertive' : 'polite'}"
class="relative overflow-hidden {class}"
class:hidden="!visible"
>
<div
class=" relative overflow-hidden rounded-2xl {compact ? 'px-4 py-3' : 'px-5 py-4 sm:px-6'} {type === 'success' ? 'bg-emerald-50 text-emerald-950 ring-1 ring-inset ring-emerald-200 dark:bg-emerald-500/10 dark:text-emerald-100 dark:ring-emerald-500/20' : type === 'warning' ? 'bg-amber-50 text-amber-950 ring-1 ring-inset ring-amber-200 dark:bg-amber-500/10 dark:text-amber-100 dark:ring-amber-500/20' : type === 'error' ? 'bg-rose-50 text-rose-950 ring-1 ring-inset ring-rose-200 dark:bg-rose-500/10 dark:text-rose-100 dark:ring-rose-500/20' : 'bg-indigo-50 text-indigo-950 ring-1 ring-inset ring-indigo-200 dark:bg-indigo-500/10 dark:text-indigo-100 dark:ring-indigo-500/20' } "
>
<!-- Decorative glow -->
<div aria-hidden="true" class=" pointer-events-none absolute -right-16 -top-20 h-40 w-40 rounded-full blur-3xl {type === 'success' ? 'bg-emerald-300/30 dark:bg-emerald-500/10' : type === 'warning' ? 'bg-amber-300/30 dark:bg-amber-500/10' : type === 'error' ? 'bg-rose-300/30 dark:bg-rose-500/10' : 'bg-indigo-300/30 dark:bg-indigo-500/10' } " ></div>
<div
class="relative flex gap-4"
class:items-center="compact"
class:items-start="!compact"
>
<!-- Icon -->
<span
class=" grid shrink-0 place-items-center rounded-xl {compact ? 'h-9 w-9' : 'h-10 w-10'} {type === 'success' ? 'bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300' : type === 'warning' ? 'bg-amber-100 text-amber-700 dark:bg-amber-500/15 dark:text-amber-300' : type === 'error' ? 'bg-rose-100 text-rose-700 dark:bg-rose-500/15 dark:text-rose-300' : 'bg-indigo-100 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300' } "
>
<span class=" h-5 w-5 {type === 'success' ? 'icon-[lucide--circle-check]' : type === 'warning' ? 'icon-[lucide--triangle-alert]' : type === 'error' ? 'icon-[lucide--circle-x]' : 'icon-[lucide--info]' } " aria-hidden="true" ></span>
</span>
<!-- Content -->
<div class="min-w-0 flex-1">
<div
class="flex flex-col gap-3 sm:flex-row sm:justify-between"
class:sm:items-center="compact"
class:sm:items-start="!compact"
>
<div class="min-w-0">
<h2
class="font-bold"
class:text-sm="compact"
class:text-base="!compact"
class:hidden="title === ''"
>
{title}
</h2>
<p
class="text-sm leading-6 opacity-80"
class:mt-1="title !== ''"
class:hidden="description === ''"
>
{description}
</p>
</div>
<!-- Actions -->
<div class="flex shrink-0 items-center gap-2">
<a
href="{actionHref}"
class=" h-9 items-center justify-center gap-1.5 rounded-lg px-3 text-xs font-bold transition {type === 'success' ? 'bg-emerald-600 text-white hover:bg-emerald-500' : type === 'warning' ? 'bg-amber-600 text-white hover:bg-amber-500' : type === 'error' ? 'bg-rose-600 text-white hover:bg-rose-500' : 'bg-indigo-600 text-white hover:bg-indigo-500' } "
class:hidden="actionLabel === ''"
class:inline-flex="actionLabel !== ''"
>
{actionLabel}
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5" aria-hidden="true" ></span>
</a>
<button
type="button"
@click="visible = false"
aria-label="Dismiss notice"
class="grid h-9 w-9 place-items-center rounded-lg opacity-60 transition hover:bg-black/5 hover:opacity-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-current dark:hover:bg-white/10"
class:hidden="!dismissible"
>
<span class="icon-[lucide--x] h-4 w-4" aria-hidden="true" ></span>
</button>
</div>
</div>
<!-- Dynamic details -->
<div
class="mt-4 flex flex-wrap gap-x-5 gap-y-2 border-t pt-4 text-xs font-medium"
class:hidden="compact || details.length === 0"
class:border-emerald-200="type === 'success'"
class:border-amber-200="type === 'warning'"
class:border-rose-200="type === 'error'"
class:border-indigo-200="type === 'info'"
class:dark:border-emerald-500/20="type === 'success'"
class:dark:border-amber-500/20="type === 'warning'"
class:dark:border-rose-500/20="type === 'error'"
class:dark:border-indigo-500/20="type === 'info'"
>
{#each details.slice(0, 4) as detail}
<span class="inline-flex items-center gap-2 opacity-80">
<span class="{detail.iconClass} h-3.5 w-3.5" aria-hidden="true" ></span>
{detail.label}
</span>
{/each}
</div>
</div>
</div>
</div>
</aside>
}
}
+212
View File
@@ -0,0 +1,212 @@
component ToastHost {
props {
class: string = ""
}
state successOpen = false
state errorOpen = false
state warningOpen = false
state infoOpen = false
state successTitle = "Changes saved"
state successMessage = "Your changes were saved successfully."
state errorTitle = "Something went wrong"
state errorMessage = "We could not complete your request. Please try again."
state warningTitle = "Action required"
state warningMessage = "Review the highlighted information before continuing."
state infoTitle = "New update available"
state infoMessage = "A newer version of this feature is now available."
view {
<div
data-toast-host
aria-live="polite"
aria-relevant="additions text"
class="pointer-events-none fixed inset-x-0 top-0 z-[120] flex flex-col items-end gap-3 p-4 sm:left-auto sm:right-0 sm:w-full sm:max-w-md sm:p-6 {class}"
>
<!-- Success toast -->
<article
role="status"
class="pointer-events-auto relative w-full overflow-hidden rounded-2xl border border-emerald-200 bg-white shadow-2xl shadow-slate-950/15 dark:border-emerald-500/20 dark:bg-slate-900 dark:shadow-black/40"
class:hidden="!successOpen"
class:block="successOpen"
>
<div class="absolute inset-y-0 left-0 w-1 bg-emerald-500"></div>
<div class="flex items-start gap-3 p-4 pl-5">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-300"
>
<span class="icon-[lucide--circle-check] h-5 w-5" aria-hidden="true" ></span>
</span>
<div class="min-w-0 flex-1 pt-0.5">
<h3 class="text-sm font-bold text-slate-950 dark:text-white">
{successTitle}
</h3>
<p class="mt-1 text-sm leading-5 text-slate-600 dark:text-slate-400">
{successMessage}
</p>
</div>
<button
type="button"
@click="successOpen = false"
aria-label="Dismiss notification"
class="grid h-8 w-8 shrink-0 place-items-center rounded-lg text-slate-400 transition hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-emerald-500 dark:hover:bg-white/10 dark:hover:text-white"
>
<span class="icon-[lucide--x] h-4 w-4" aria-hidden="true" ></span>
</button>
</div>
<div class="h-1 w-full bg-emerald-100 dark:bg-emerald-500/10">
<div class="h-full w-full origin-left bg-emerald-500"></div>
</div>
</article>
<!-- Error toast -->
<article
role="alert"
class="pointer-events-auto relative w-full overflow-hidden rounded-2xl border border-rose-200 bg-white shadow-2xl shadow-slate-950/15 dark:border-rose-500/20 dark:bg-slate-900 dark:shadow-black/40"
class:hidden="!errorOpen"
class:block="errorOpen"
>
<div class="absolute inset-y-0 left-0 w-1 bg-rose-500"></div>
<div class="flex items-start gap-3 p-4 pl-5">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl bg-rose-100 text-rose-700 dark:bg-rose-500/15 dark:text-rose-300"
>
<span class="icon-[lucide--circle-x] h-5 w-5" aria-hidden="true" ></span>
</span>
<div class="min-w-0 flex-1 pt-0.5">
<h3 class="text-sm font-bold text-slate-950 dark:text-white">
{errorTitle}
</h3>
<p class="mt-1 text-sm leading-5 text-slate-600 dark:text-slate-400">
{errorMessage}
</p>
<button
type="button"
class="mt-3 inline-flex items-center gap-1.5 text-xs font-bold text-rose-700 transition hover:text-rose-900 dark:text-rose-300 dark:hover:text-rose-200"
>
Try again
<span class="icon-[lucide--rotate-cw] h-3.5 w-3.5" aria-hidden="true" ></span>
</button>
</div>
<button
type="button"
@click="errorOpen = false"
aria-label="Dismiss notification"
class="grid h-8 w-8 shrink-0 place-items-center rounded-lg text-slate-400 transition hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-rose-500 dark:hover:bg-white/10 dark:hover:text-white"
>
<span class="icon-[lucide--x] h-4 w-4" aria-hidden="true" ></span>
</button>
</div>
<div class="h-1 w-full bg-rose-100 dark:bg-rose-500/10">
<div class="h-full w-full origin-left bg-rose-500"></div>
</div>
</article>
<!-- Warning toast -->
<article
role="status"
class="pointer-events-auto relative w-full overflow-hidden rounded-2xl border border-amber-200 bg-white shadow-2xl shadow-slate-950/15 dark:border-amber-500/20 dark:bg-slate-900 dark:shadow-black/40"
class:hidden="!warningOpen"
class:block="warningOpen"
>
<div class="absolute inset-y-0 left-0 w-1 bg-amber-500"></div>
<div class="flex items-start gap-3 p-4 pl-5">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl bg-amber-100 text-amber-700 dark:bg-amber-500/15 dark:text-amber-300"
>
<span class="icon-[lucide--triangle-alert] h-5 w-5" aria-hidden="true" ></span>
</span>
<div class="min-w-0 flex-1 pt-0.5">
<h3 class="text-sm font-bold text-slate-950 dark:text-white">
{warningTitle}
</h3>
<p class="mt-1 text-sm leading-5 text-slate-600 dark:text-slate-400">
{warningMessage}
</p>
</div>
<button
type="button"
@click="warningOpen = false"
aria-label="Dismiss notification"
class="grid h-8 w-8 shrink-0 place-items-center rounded-lg text-slate-400 transition hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-amber-500 dark:hover:bg-white/10 dark:hover:text-white"
>
<span class="icon-[lucide--x] h-4 w-4" aria-hidden="true" ></span>
</button>
</div>
<div class="h-1 w-full bg-amber-100 dark:bg-amber-500/10">
<div class="h-full w-full origin-left bg-amber-500"></div>
</div>
</article>
<!-- Information toast -->
<article
role="status"
class="pointer-events-auto relative w-full overflow-hidden rounded-2xl border border-indigo-200 bg-white shadow-2xl shadow-slate-950/15 dark:border-indigo-500/20 dark:bg-slate-900 dark:shadow-black/40"
class:hidden="!infoOpen"
class:block="infoOpen"
>
<div class="absolute inset-y-0 left-0 w-1 bg-indigo-500"></div>
<div class="flex items-start gap-3 p-4 pl-5">
<span
class="grid h-10 w-10 shrink-0 place-items-center rounded-xl bg-indigo-100 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300"
>
<span class="icon-[lucide--info] h-5 w-5" aria-hidden="true" ></span>
</span>
<div class="min-w-0 flex-1 pt-0.5">
<h3 class="text-sm font-bold text-slate-950 dark:text-white">
{infoTitle}
</h3>
<p class="mt-1 text-sm leading-5 text-slate-600 dark:text-slate-400">
{infoMessage}
</p>
<a
href="/changelog"
class="mt-3 inline-flex items-center gap-1.5 text-xs font-bold text-indigo-700 transition hover:text-indigo-900 dark:text-indigo-300 dark:hover:text-indigo-200"
>
View update
<span class="icon-[lucide--arrow-up-right] h-3.5 w-3.5" aria-hidden="true" ></span>
</a>
</div>
<button
type="button"
@click="infoOpen = false"
aria-label="Dismiss notification"
class="grid h-8 w-8 shrink-0 place-items-center rounded-lg text-slate-400 transition hover:bg-slate-100 hover:text-slate-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:hover:bg-white/10 dark:hover:text-white"
>
<span class="icon-[lucide--x] h-4 w-4" aria-hidden="true" ></span>
</button>
</div>
<div class="h-1 w-full bg-indigo-100 dark:bg-indigo-500/10">
<div class="h-full w-full origin-left bg-indigo-500"></div>
</div>
</article>
</div>
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ui",
"version": "0.2.61",
"version": "0.2.62",
"private": true,
"type": "module",
"main": "src/index.ts",
+3
View File
@@ -79,6 +79,9 @@ test("component-system CSS includes responsive, theme-token, focus, and reduced-
expect(css).toContain("--color-violet-600: var(--wire-color-primary)");
expect(css).toContain("--color-blue-600: var(--wire-color-info)");
expect(css).toContain("--color-red-600: var(--wire-color-danger)");
expect(css).toMatch(/:root\s*\{[^}]*--color-violet-600: var\(--wire-color-primary\)/s);
expect(css).toContain(".wire-bg-primary");
expect(css).toContain(".wire-text-muted");
});
test("page shells leave background ownership to the page", () => {
+45 -3
View File
@@ -1050,9 +1050,10 @@
--wire-ease-emphasized: cubic-bezier(0.16, 1, 0.3, 1);
}
/* Tailwind v4 color bridge. Existing utility-authored components inherit the
* configured semantic palette without requiring generated per-palette markup. */
[data-component] {
/* Tailwind v4 color bridge. Define the aliases at the document root so both
* packaged components and application-authored pages/components inherit the
* palette selected in wrnexus.config.ts. */
:root {
--color-violet-50: color-mix(in srgb, var(--wire-color-primary) 7%, white);
--color-violet-100: color-mix(in srgb, var(--wire-color-primary) 14%, white);
--color-violet-200: color-mix(in srgb, var(--wire-color-primary) 25%, white);
@@ -1096,6 +1097,47 @@
--color-rose-700: color-mix(in srgb, var(--wire-color-danger) 80%, black);
}
/* Stable semantic helpers for application-authored markup. These deliberately
* avoid spacing so applications remain in control of their own layout. */
.wire-bg-page {
background-color: var(--wire-color-bg);
}
.wire-bg-surface {
background-color: var(--wire-color-surface);
}
.wire-bg-surface-2 {
background-color: var(--wire-color-surface-2);
}
.wire-bg-primary {
background-color: var(--wire-color-primary);
color: var(--wire-color-primary-contrast);
}
.wire-bg-secondary {
background-color: var(--wire-color-secondary);
color: var(--wire-color-secondary-contrast);
}
.wire-text {
color: var(--wire-color-text);
}
.wire-text-muted {
color: var(--wire-color-muted);
}
.wire-text-primary {
color: var(--wire-color-primary);
}
.wire-text-success {
color: var(--wire-color-success);
}
.wire-text-warning {
color: var(--wire-color-warning);
}
.wire-text-danger {
color: var(--wire-color-danger);
}
.wire-border {
border-color: var(--wire-color-border);
}
[data-component] {
animation: wire-component-enter var(--wire-motion-slow) var(--wire-ease-emphasized) both;
}

Some files were not shown because too many files have changed in this diff Show More