fix: normalize component boundary spacing

This commit is contained in:
2026-07-19 20:08:14 +05:30
parent eaea2c5f72
commit c5326f7622
191 changed files with 1280 additions and 351 deletions
+64 -64
View File
@@ -297,7 +297,7 @@ Reusable hero secondary action component.
Reusable icon button component.
- Mount: `data-component="IconButton"`
- Props: `label: string = "Action"`, `icon: string = "•"`, `variant: string = "ghost"`, `disabled: boolean = false`
- Props: `class: string = ""`, `label: string = "Action"`, `icon: string = "•"`, `variant: string = "ghost"`, `disabled: boolean = false`
- Slots: None
- Events: None
@@ -650,7 +650,7 @@ Reusable accessible tooltip component.
Reusable accordion component.
- Mount: `data-component="Accordion"`
- Props: `title: string = "Question"`, `open: boolean = false`
- Props: `class: string = ""`, `title: string = "Question"`, `open: boolean = false`
- Slots: `default`
- Events: `click`
@@ -695,7 +695,7 @@ Reusable analytics dashboard preview component.
Reusable api endpoint card component.
- Mount: `data-component="ApiEndpointCard"`
- Props: `method: string = "GET"`, `path: string = "/api/example"`, `description: string = ""`
- Props: `class: string = ""`, `method: string = "GET"`, `path: string = "/api/example"`, `description: string = ""`
- Slots: `default`
- Events: None
@@ -830,7 +830,7 @@ Reusable availability calendar component.
Reusable avatar component.
- Mount: `data-component="Avatar"`
- Props: `src: string = ""`, `alt: string = ""`, `initials: string = "WR"`, `size: string = "md"`
- Props: `class: string = ""`, `src: string = ""`, `alt: string = ""`, `initials: string = "WR"`, `size: string = "md"`
- Slots: None
- Events: None
@@ -1055,7 +1055,7 @@ Reusable code component.
Reusable code block component.
- Mount: `data-component="CodeBlock"`
- Props: `language: string = "text"`, `filename: string = ""`, `code: string = ""`
- Props: `class: string = ""`, `language: string = "text"`, `filename: string = ""`, `code: string = ""`
- Slots: None
- Events: `click`
@@ -1460,7 +1460,7 @@ Reusable favicon upload component.
Reusable feature card component.
- Mount: `data-component="FeatureCard"`
- Props: `icon: string = "✦"`, `title: string = "Feature"`, `description: string = ""`, `href: string = ""`
- Props: `class: string = ""`, `icon: string = "✦"`, `title: string = "Feature"`, `description: string = ""`, `href: string = ""`
- Slots: None
- Events: None
@@ -1514,7 +1514,7 @@ Reusable file size label component.
Reusable file upload component.
- Mount: `data-component="FileUpload"`
- Props: `id: string = "file"`, `name: string = "file"`, `label: string = "Upload file"`, `accept: string = ""`, `multiple: boolean = false`, `help: string = "Drag and drop or browse"`
- Props: `class: string = ""`, `id: string = "file"`, `name: string = "file"`, `label: string = "Upload file"`, `accept: string = ""`, `multiple: boolean = false`, `help: string = "Drag and drop or browse"`
- Slots: None
- Events: None
@@ -1559,7 +1559,7 @@ Reusable form component.
Reusable form error summary component.
- Mount: `data-component="FormErrorSummary"`
- Props: `title: string = "Please fix the following"`, `visible: boolean = true`
- Props: `class: string = ""`, `title: string = "Please fix the following"`, `visible: boolean = true`
- Slots: `default`
- Events: None
@@ -2135,7 +2135,7 @@ Reusable pricing contact form component.
Reusable pricing plan card component.
- Mount: `data-component="PricingPlanCard"`
- Props: `name: string = "Starter"`, `description: string = ""`, `price: string = "₹0"`, `period: string = "/month"`, `featured: boolean = false`, `ctaLabel: string = "Choose plan"`, `ctaHref: string = "#"`
- Props: `class: string = ""`, `name: string = "Starter"`, `description: string = ""`, `price: string = "₹0"`, `period: string = "/month"`, `featured: boolean = false`, `ctaLabel: string = "Choose plan"`, `ctaHref: string = "#"`
- Slots: `default`
- Events: None
@@ -2531,7 +2531,7 @@ Reusable sms composer component.
Reusable stat card component.
- Mount: `data-component="StatCard"`
- Props: `label: string = "Metric"`, `value: string = "0"`, `change: string = ""`, `trend: string = "neutral"`
- Props: `class: string = ""`, `label: string = "Metric"`, `value: string = "0"`, `change: string = ""`, `trend: string = "neutral"`
- Slots: None
- Events: None
@@ -2576,7 +2576,7 @@ Reusable support request form component.
Reusable tabs component.
- Mount: `data-component="Tabs"`
- Props: `active: string = "first"`
- Props: `class: string = ""`, `active: string = "first"`
- Slots: `first`, `second`
- Events: `click`
@@ -2612,7 +2612,7 @@ Reusable terminal block component.
Reusable testimonial card component.
- Mount: `data-component="TestimonialCard"`
- Props: `quote: string = "Great product."`, `name: string = "Customer"`, `role: string = ""`, `company: string = ""`, `avatar: string = ""`
- Props: `class: string = ""`, `quote: string = "Great product."`, `name: string = "Customer"`, `role: string = ""`, `company: string = ""`, `avatar: string = ""`
- Slots: None
- Events: None
@@ -2639,7 +2639,7 @@ Reusable theme switcher component.
Reusable timeline item component.
- Mount: `data-component="TimelineItem"`
- Props: `title: string = "Event"`, `date: string = ""`, `description: string = ""`, `status: string = "default"`
- Props: `class: string = ""`, `title: string = "Event"`, `date: string = ""`, `description: string = ""`, `status: string = "default"`
- Slots: None
- Events: None
@@ -2657,7 +2657,7 @@ Reusable toast icon component.
Reusable tooltip component.
- Mount: `data-component="Tooltip"`
- Props: `text: string = "Helpful information"`, `position: string = "top"`
- Props: `class: string = ""`, `text: string = "Helpful information"`, `position: string = "top"`
- Slots: `default`
- Events: None
@@ -2884,7 +2884,7 @@ Reusable zoom slider component.
Reusable announcement bar component.
- Mount: `data-component="AnnouncementBar"`
- Props: `badge: string = "New"`, `message: string = "WrNexus Organizations is now available."`, `description: string = "Build secure multi-tenant applications with teams, roles, domains, and enterprise SSO."`, `href: string = "/organizations"`, `actionLabel: string = "Explore organizations"`, `ariaLabel: string = "Announcement"`
- Props: `class: string = ""`, `badge: string = "New"`, `message: string = "WrNexus Organizations is now available."`, `description: string = "Build secure multi-tenant applications with teams, roles, domains, and enterprise SSO."`, `href: string = "/organizations"`, `actionLabel: string = "Explore organizations"`, `ariaLabel: string = "Announcement"`
- Slots: None
- Events: `click`
@@ -2902,7 +2902,7 @@ Reusable aspect ratio component.
Reusable back to top component.
- Mount: `data-component="BackToTop"`
- Props: `label: string = "Back to top"`, `assistiveLabel: string = "Return to the top of the page"`, `threshold: number = 500`
- Props: `class: string = ""`, `label: string = "Back to top"`, `assistiveLabel: string = "Return to the top of the page"`, `threshold: number = 500`
- Slots: None
- Events: `click`
@@ -2911,7 +2911,7 @@ Reusable back to top component.
Reusable breadcrumbs component.
- Mount: `data-component="Breadcrumbs"`
- Props: `homeLabel: string = "Home"`, `homeHref: string = "/"`, `current: string = "Current page"`
- Props: `class: string = ""`, `homeLabel: string = "Home"`, `homeHref: string = "/"`, `current: string = "Current page"`
- Slots: None
- Events: None
@@ -2920,7 +2920,7 @@ Reusable breadcrumbs component.
Reusable checkbox component.
- Mount: `data-component="Checkbox"`
- Props: `id: string = ""`, `name: string = ""`, `label: string = "Checkbox"`, `description: string = ""`, `checked: boolean = false`, `disabled: boolean = false`
- Props: `class: string = ""`, `id: string = ""`, `name: string = ""`, `label: string = "Checkbox"`, `description: string = ""`, `checked: boolean = false`, `disabled: boolean = false`
- Slots: None
- Events: None
@@ -2992,7 +2992,7 @@ Reusable divider component.
Reusable drawer component.
- Mount: `data-component="Drawer"`
- Props: `title: string = "Panel"`, `open: boolean = false`, `side: string = "right"`
- Props: `class: string = ""`, `title: string = "Panel"`, `open: boolean = false`, `side: string = "right"`
- Slots: `default`
- Events: `click`
@@ -3001,7 +3001,7 @@ Reusable drawer component.
Reusable faq component.
- Mount: `data-component="FAQ"`
- Props: `eyebrow: string = ""`, `title: string = "Frequently asked questions"`, `description: string = ""`, `items: string = []`, `centered: boolean = true`, `allowMultiple: boolean = false`, `defaultOpenIndex: number = 0`
- Props: `class: string = ""`, `eyebrow: string = ""`, `title: string = "Frequently asked questions"`, `description: string = ""`, `items: string = []`, `centered: boolean = true`, `allowMultiple: boolean = false`, `defaultOpenIndex: number = 0`
- Slots: None
- Events: None
@@ -3010,7 +3010,7 @@ Reusable faq component.
Reusable faqaccordion component.
- Mount: `data-component="FAQAccordion"`
- Props: `eyebrow: string = ""`, `title: string = "Frequently asked questions"`, `description: string = ""`, `items: string = []`, `centered: boolean = true`, `compact: boolean = false`, `allowMultiple: boolean = false`, `defaultOpenIndex: number = 0`, `showContact: boolean = false`, `contactText: string = "Still have questions?"`, `contactLabel: string = "Contact support"`, `contactHref: string = "/support"`
- Props: `class: string = ""`, `eyebrow: string = ""`, `title: string = "Frequently asked questions"`, `description: string = ""`, `items: string = []`, `centered: boolean = true`, `compact: boolean = false`, `allowMultiple: boolean = false`, `defaultOpenIndex: number = 0`, `showContact: boolean = false`, `contactText: string = "Still have questions?"`, `contactLabel: string = "Contact support"`, `contactHref: string = "/support"`
- Slots: None
- Events: `click`
@@ -3109,7 +3109,7 @@ Reusable link component.
Reusable logo cloud component.
- Mount: `data-component="LogoCloud"`
- Props: `title: string = "Trusted by modern teams"`
- Props: `class: string = ""`, `title: string = "Trusted by modern teams"`
- Slots: `default`
- Events: None
@@ -3118,7 +3118,7 @@ Reusable logo cloud component.
Reusable modal component.
- Mount: `data-component="Modal"`
- Props: `title: string = "Dialog"`, `description: string = ""`, `open: boolean = false`, `size: string = "md"`, `closeLabel: string = "Close"`
- Props: `class: string = ""`, `title: string = "Dialog"`, `description: string = ""`, `open: boolean = false`, `size: string = "md"`, `closeLabel: string = "Close"`
- Slots: `default`
- Events: `click`
@@ -3172,7 +3172,7 @@ Reusable page shell component.
Reusable pagination component.
- Mount: `data-component="Pagination"`
- Props: `page: number = 1`, `totalPages: number = 1`, `previousHref: string = "#"`, `nextHref: string = "#"`
- Props: `class: string = ""`, `page: number = 1`, `totalPages: number = 1`, `previousHref: string = "#"`, `nextHref: string = "#"`
- Slots: None
- Events: None
@@ -3199,7 +3199,7 @@ Reusable progress component.
Reusable progress bar component.
- Mount: `data-component="ProgressBar"`
- Props: `value: number = 0`, `max: number = 100`, `label: string = "Progress"`, `showValue: boolean = true`
- Props: `class: string = ""`, `value: number = 0`, `max: number = 100`, `label: string = "Progress"`, `showValue: boolean = true`
- Slots: None
- Events: None
@@ -3208,7 +3208,7 @@ Reusable progress bar component.
Reusable public footer component.
- Mount: `data-component="PublicFooter"`
- Props: `brand: string = "WRNexusJS"`, `description: string = "SSR-first, Bun-native full-stack framework."`, `copyright: string = "© 2026 WorkRoot Workspace"`
- Props: `class: string = ""`, `brand: string = "WRNexusJS"`, `description: string = "SSR-first, Bun-native full-stack framework."`, `copyright: string = "© 2026 WorkRoot Workspace"`
- Slots: `default`
- Events: None
@@ -3217,7 +3217,7 @@ Reusable public footer component.
Reusable radio component.
- Mount: `data-component="Radio"`
- Props: `id: string = ""`, `name: string = "choice"`, `value: string = ""`, `label: string = "Option"`, `description: string = ""`, `checked: boolean = false`, `disabled: boolean = false`
- Props: `class: string = ""`, `id: string = ""`, `name: string = "choice"`, `value: string = ""`, `label: string = "Option"`, `description: string = ""`, `checked: boolean = false`, `disabled: boolean = false`
- Slots: None
- Events: None
@@ -3253,7 +3253,7 @@ Reusable section heading component.
Reusable select component.
- Mount: `data-component="Select"`
- Props: `id: string = ""`, `name: string = ""`, `label: string = "Select"`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`
- Props: `class: string = ""`, `id: string = ""`, `name: string = ""`, `label: string = "Select"`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`
- Slots: `default`
- Events: None
@@ -3271,7 +3271,7 @@ Reusable spacer component.
Reusable spinner component.
- Mount: `data-component="Spinner"`
- Props: `label: string = "Loading"`, `size: string = "md"`
- Props: `class: string = ""`, `label: string = "Loading"`, `size: string = "md"`
- Slots: None
- Events: None
@@ -3316,7 +3316,7 @@ Reusable surface component.
Reusable switch component.
- Mount: `data-component="Switch"`
- Props: `id: string = ""`, `name: string = ""`, `label: string = "Switch"`, `description: string = ""`, `checked: boolean = false`, `disabled: boolean = false`
- Props: `class: string = ""`, `id: string = ""`, `name: string = ""`, `label: string = "Switch"`, `description: string = ""`, `checked: boolean = false`, `disabled: boolean = false`
- Slots: None
- Events: `click`
@@ -3334,7 +3334,7 @@ Reusable tag component.
Reusable textarea component.
- Mount: `data-component="Textarea"`
- Props: `id: string = ""`, `name: string = ""`, `label: string = "Message"`, `value: string = ""`, `placeholder: string = ""`, `rows: number = 5`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`
- Props: `class: string = ""`, `id: string = ""`, `name: string = ""`, `label: string = "Message"`, `value: string = ""`, `placeholder: string = ""`, `rows: number = 5`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`
- Slots: None
- Events: None
@@ -3352,7 +3352,7 @@ Reusable theme-toggle component.
Reusable toast component.
- Mount: `data-component="Toast"`
- Props: `title: string = "Saved"`, `description: string = ""`, `variant: string = "success"`, `duration: number = 5000`
- Props: `class: string = ""`, `title: string = "Saved"`, `description: string = ""`, `variant: string = "success"`, `duration: number = 5000`
- Slots: None
- Events: `click`
@@ -4047,7 +4047,7 @@ Reusable subprocessor table component.
Reusable table component.
- Mount: `data-component="Table"`
- Props: `caption: string = "Data table"`, `responsive: boolean = true`
- Props: `class: string = ""`, `caption: string = "Data table"`, `responsive: boolean = true`
- Slots: `default`
- Events: None
@@ -4121,7 +4121,7 @@ Reusable account status banner component.
Reusable alert component.
- Mount: `data-component="Alert"`
- Props: `title: string = "Notice"`, `description: string = ""`, `variant: string = "info"`, `dismissible: boolean = false`
- Props: `class: string = ""`, `title: string = "Notice"`, `description: string = ""`, `variant: string = "info"`, `dismissible: boolean = false`
- Slots: `default`
- Events: `click`
@@ -4166,7 +4166,7 @@ Reusable api version badge component.
Reusable badge component.
- Mount: `data-component="Badge"`
- Props: `label: string = "Badge"`, `variant: string = "neutral"`
- Props: `class: string = ""`, `label: string = "Badge"`, `variant: string = "neutral"`
- Slots: None
- Events: None
@@ -4175,7 +4175,7 @@ Reusable badge component.
Reusable banner component.
- Mount: `data-component="Banner"`
- Props: `text: string = "Announcement"`, `href: string = ""`, `actionLabel: string = "Learn more"`, `variant: string = "brand"`
- Props: `class: string = ""`, `text: string = "Announcement"`, `href: string = ""`, `actionLabel: string = "Learn more"`, `variant: string = "brand"`
- Slots: None
- Events: None
@@ -4265,7 +4265,7 @@ Reusable coming soon state component.
Reusable cookie banner component.
- Mount: `data-component="CookieBanner"`
- Props: `title: string = "We use cookies"`, `description: string = "We use essential cookies and optional analytics to improve your experience."`, `privacyHref: string = "/privacy"`
- Props: `class: string = ""`, `title: string = "We use cookies"`, `description: string = "We use essential cookies and optional analytics to improve your experience."`, `privacyHref: string = "/privacy"`
- Slots: None
- Events: `click`
@@ -4292,7 +4292,7 @@ Reusable deprecated feature alert component.
Reusable empty state component.
- Mount: `data-component="EmptyState"`
- Props: `icon: string = "◇"`, `title: string = "Nothing here yet"`, `description: string = ""`, `actionLabel: string = ""`, `actionHref: string = "#"`
- Props: `class: string = ""`, `icon: string = "◇"`, `title: string = "Nothing here yet"`, `description: string = ""`, `actionLabel: string = ""`, `actionHref: string = "#"`
- Slots: None
- Events: None
@@ -4301,7 +4301,7 @@ Reusable empty state component.
Reusable error state component.
- Mount: `data-component="ErrorState"`
- Props: `code: string = "500"`, `title: string = "Something went wrong"`, `description: string = "Please try again."`, `actionLabel: string = "Try again"`, `actionHref: string = "/"`
- Props: `class: string = ""`, `code: string = "500"`, `title: string = "Something went wrong"`, `description: string = "Please try again."`, `actionLabel: string = "Try again"`, `actionHref: string = "/"`
- Slots: None
- Events: None
@@ -4688,7 +4688,7 @@ Reusable sms segment counter component.
Reusable status badge component.
- Mount: `data-component="StatusBadge"`
- Props: `status: string = "operational"`
- Props: `class: string = ""`, `status: string = "operational"`
- Slots: None
- Events: None
@@ -5167,7 +5167,7 @@ Reusable current password input component.
Reusable date input component.
- Mount: `data-component="DateInput"`
- Props: `id: string = ""`, `name: string = ""`, `label: string = "Date"`, `value: string = ""`, `placeholder: string = ""`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`, `readonly: boolean = false`, `autocomplete: string = ""`
- Props: `class: string = ""`, `id: string = ""`, `name: string = ""`, `label: string = "Date"`, `value: string = ""`, `placeholder: string = ""`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`, `readonly: boolean = false`, `autocomplete: string = ""`
- Slots: None
- Events: None
@@ -5176,7 +5176,7 @@ Reusable date input component.
Reusable date picker component.
- Mount: `data-component="DatePicker"`
- Props: `id: string = "date"`, `name: string = "date"`, `label: string = "Date"`, `value: string = ""`, `min: string = ""`, `max: string = ""`, `required: boolean = false`
- Props: `class: string = ""`, `id: string = "date"`, `name: string = "date"`, `label: string = "Date"`, `value: string = ""`, `min: string = ""`, `max: string = ""`, `required: boolean = false`
- Slots: None
- Events: None
@@ -5185,7 +5185,7 @@ Reusable date picker component.
Reusable date range picker component.
- Mount: `data-component="DateRangePicker"`
- Props: `label: string = "Date range"`, `startName: string = "startDate"`, `endName: string = "endDate"`
- Props: `class: string = ""`, `label: string = "Date range"`, `startName: string = "startDate"`, `endName: string = "endDate"`
- Slots: None
- Events: None
@@ -5194,7 +5194,7 @@ Reusable date range picker component.
Reusable date time input component.
- Mount: `data-component="DateTimeInput"`
- Props: `id: string = ""`, `name: string = ""`, `label: string = "DateTime"`, `value: string = ""`, `placeholder: string = ""`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`, `readonly: boolean = false`, `autocomplete: string = ""`
- Props: `class: string = ""`, `id: string = ""`, `name: string = ""`, `label: string = "DateTime"`, `value: string = ""`, `placeholder: string = ""`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`, `readonly: boolean = false`, `autocomplete: string = ""`
- Slots: None
- Events: None
@@ -5293,7 +5293,7 @@ Reusable email body editor component.
Reusable email input component.
- Mount: `data-component="EmailInput"`
- Props: `id: string = ""`, `name: string = ""`, `label: string = "Email"`, `value: string = ""`, `placeholder: string = ""`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`, `readonly: boolean = false`, `autocomplete: string = ""`
- Props: `class: string = ""`, `id: string = ""`, `name: string = ""`, `label: string = "Email"`, `value: string = ""`, `placeholder: string = ""`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`, `readonly: boolean = false`, `autocomplete: string = ""`
- Slots: None
- Events: None
@@ -5401,7 +5401,7 @@ Reusable font weight picker component.
Reusable form field component.
- Mount: `data-component="FormField"`
- Props: `label: string = "Field"`, `help: string = ""`, `error: string = ""`, `required: boolean = false`
- Props: `class: string = ""`, `label: string = "Field"`, `help: string = ""`, `error: string = ""`, `required: boolean = false`
- Slots: `default`
- Events: None
@@ -5824,7 +5824,7 @@ Reusable new password input component.
Reusable number input component.
- Mount: `data-component="NumberInput"`
- Props: `id: string = ""`, `name: string = ""`, `label: string = "Number"`, `value: string = ""`, `placeholder: string = ""`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`, `readonly: boolean = false`, `autocomplete: string = ""`
- Props: `class: string = ""`, `id: string = ""`, `name: string = ""`, `label: string = "Number"`, `value: string = ""`, `placeholder: string = ""`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`, `readonly: boolean = false`, `autocomplete: string = ""`
- Slots: None
- Events: None
@@ -5842,7 +5842,7 @@ Reusable organization selector component.
Reusable otp input component.
- Mount: `data-component="OtpInput"`
- Props: `name: string = "otp"`, `length: number = 6`, `label: string = "Verification code"`
- Props: `class: string = ""`, `name: string = "otp"`, `length: number = 6`, `label: string = "Verification code"`
- Slots: None
- Events: None
@@ -5851,7 +5851,7 @@ Reusable otp input component.
Reusable password input component.
- Mount: `data-component="PasswordInput"`
- Props: `id: string = ""`, `name: string = ""`, `label: string = "Password"`, `value: string = ""`, `placeholder: string = ""`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`, `readonly: boolean = false`, `autocomplete: string = ""`
- Props: `class: string = ""`, `id: string = ""`, `name: string = ""`, `label: string = "Password"`, `value: string = ""`, `placeholder: string = ""`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`, `readonly: boolean = false`, `autocomplete: string = ""`
- Slots: None
- Events: None
@@ -5896,7 +5896,7 @@ Reusable permission selector component.
Reusable phone input component.
- Mount: `data-component="PhoneInput"`
- Props: `id: string = ""`, `name: string = ""`, `label: string = "Phone"`, `value: string = ""`, `placeholder: string = ""`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`, `readonly: boolean = false`, `autocomplete: string = ""`
- Props: `class: string = ""`, `id: string = ""`, `name: string = ""`, `label: string = "Phone"`, `value: string = ""`, `placeholder: string = ""`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`, `readonly: boolean = false`, `autocomplete: string = ""`
- Slots: None
- Events: None
@@ -6085,7 +6085,7 @@ Reusable quiet hours picker component.
Reusable radio group component.
- Mount: `data-component="RadioGroup"`
- Props: `label: string = "Choose one"`, `name: string = "choice"`
- Props: `class: string = ""`, `label: string = "Choose one"`, `name: string = "choice"`
- Slots: `default`
- Events: None
@@ -6256,7 +6256,7 @@ Reusable schedule message picker component.
Reusable schedule picker component.
- Mount: `data-component="SchedulePicker"`
- Props: `label: string = "Schedule"`
- Props: `class: string = ""`, `label: string = "Schedule"`
- Slots: None
- Events: None
@@ -6274,7 +6274,7 @@ Reusable sdk language selector component.
Reusable search input component.
- Mount: `data-component="SearchInput"`
- Props: `id: string = ""`, `name: string = ""`, `label: string = "Search"`, `value: string = ""`, `placeholder: string = ""`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`, `readonly: boolean = false`, `autocomplete: string = ""`
- Props: `class: string = ""`, `id: string = ""`, `name: string = ""`, `label: string = "Search"`, `value: string = ""`, `placeholder: string = ""`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`, `readonly: boolean = false`, `autocomplete: string = ""`
- Slots: None
- Events: None
@@ -6454,7 +6454,7 @@ Reusable template variable input component.
Reusable text input component.
- Mount: `data-component="TextInput"`
- Props: `id: string = ""`, `name: string = ""`, `label: string = "Text"`, `value: string = ""`, `placeholder: string = ""`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`, `readonly: boolean = false`, `autocomplete: string = ""`
- Props: `class: string = ""`, `id: string = ""`, `name: string = ""`, `label: string = "Text"`, `value: string = ""`, `placeholder: string = ""`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`, `readonly: boolean = false`, `autocomplete: string = ""`
- Slots: None
- Events: None
@@ -6472,7 +6472,7 @@ Reusable theme color picker component.
Reusable time input component.
- Mount: `data-component="TimeInput"`
- Props: `id: string = ""`, `name: string = ""`, `label: string = "Time"`, `value: string = ""`, `placeholder: string = ""`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`, `readonly: boolean = false`, `autocomplete: string = ""`
- Props: `class: string = ""`, `id: string = ""`, `name: string = ""`, `label: string = "Time"`, `value: string = ""`, `placeholder: string = ""`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`, `readonly: boolean = false`, `autocomplete: string = ""`
- Slots: None
- Events: None
@@ -6517,7 +6517,7 @@ Reusable timezone aware time picker component.
Reusable timezone selector component.
- Mount: `data-component="TimezoneSelector"`
- Props: `id: string = "timezone"`, `name: string = "timezone"`, `label: string = "Timezone"`
- Props: `class: string = ""`, `id: string = "timezone"`, `name: string = "timezone"`, `label: string = "Timezone"`
- Slots: None
- Events: None
@@ -6553,7 +6553,7 @@ Reusable token input component.
Reusable url input component.
- Mount: `data-component="UrlInput"`
- Props: `id: string = ""`, `name: string = ""`, `label: string = "Url"`, `value: string = ""`, `placeholder: string = ""`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`, `readonly: boolean = false`, `autocomplete: string = ""`
- Props: `class: string = ""`, `id: string = ""`, `name: string = ""`, `label: string = "Url"`, `value: string = ""`, `placeholder: string = ""`, `help: string = ""`, `error: string = ""`, `required: boolean = false`, `disabled: boolean = false`, `readonly: boolean = false`, `autocomplete: string = ""`
- Slots: None
- Events: None
@@ -6834,7 +6834,7 @@ Reusable contact page shell component.
Reusable c t a section component.
- Mount: `data-component="CTASection"`
- Props: `eyebrow: string = "Ready?"`, `title: string = "Start building today"`, `description: string = ""`, `primaryLabel: string = "Get started"`, `primaryHref: string = "#"`, `secondaryLabel: string = "Contact sales"`, `secondaryHref: string = "#"`
- Props: `class: string = ""`, `eyebrow: string = "Ready?"`, `title: string = "Start building today"`, `description: string = ""`, `primaryLabel: string = "Get started"`, `primaryHref: string = "#"`, `secondaryLabel: string = "Contact sales"`, `secondaryHref: string = "#"`
- Slots: None
- Events: None
@@ -6933,7 +6933,7 @@ Reusable header component.
Reusable hero component.
- Mount: `data-component="Hero"`
- Props: `eyebrow: string = ""`, `title: string = "Build faster with WRNexusJS"`, `highlight: string = ""`, `description: string = ""`, `primaryLabel: string = "Get started"`, `primaryHref: string = "#"`, `secondaryLabel: string = "Learn more"`, `secondaryHref: string = "#"`, `align: string = "center"`
- Props: `class: string = ""`, `eyebrow: string = ""`, `title: string = "Build faster with WRNexusJS"`, `highlight: string = ""`, `description: string = ""`, `primaryLabel: string = "Get started"`, `primaryHref: string = "#"`, `secondaryLabel: string = "Learn more"`, `secondaryHref: string = "#"`, `align: string = "center"`
- Slots: `default`
- Events: None
@@ -6969,7 +6969,7 @@ Reusable legal document header component.
Reusable legal document layout component.
- Mount: `data-component="LegalDocumentLayout"`
- Props: `title: string = "Legal document"`, `effectiveDate: string = ""`, `updatedDate: string = ""`
- Props: `class: string = ""`, `title: string = "Legal document"`, `effectiveDate: string = ""`, `updatedDate: string = ""`
- Slots: `toc`, `default`
- Events: None
@@ -7113,7 +7113,7 @@ Reusable product page shell component.
Reusable public header component.
- Mount: `data-component="PublicHeader"`
- Props: `brand: string = "WRNexusJS"`, `homeHref: string = "/"`, `signInHref: string = "/signin"`, `ctaHref: string = "/start"`, `ctaLabel: string = "Get started"`
- Props: `class: string = ""`, `brand: string = "WRNexusJS"`, `homeHref: string = "/"`, `signInHref: string = "/signin"`, `ctaHref: string = "/start"`, `ctaLabel: string = "Get started"`
- Slots: `default`, `mobile`
- Events: `click`
@@ -7176,7 +7176,7 @@ Reusable section component.
Reusable section header component.
- Mount: `data-component="SectionHeader"`
- Props: `eyebrow: string = ""`, `title: string = "Section title"`, `description: string = ""`, `align: string = "left"`
- Props: `class: string = ""`, `eyebrow: string = ""`, `title: string = "Section title"`, `description: string = ""`, `align: string = "left"`
- Slots: `default`
- Events: None
@@ -7439,7 +7439,7 @@ Reusable developers menu component.
Reusable dropdown menu component.
- Mount: `data-component="DropdownMenu"`
- Props: `label: string = "Menu"`
- Props: `class: string = ""`, `label: string = "Menu"`
- Slots: `default`
- Events: `click`
@@ -7972,7 +7972,7 @@ Reusable tenant isolation diagram component.
Reusable timeline component.
- Mount: `data-component="Timeline"`
- Props: `title: string = "Timeline"`
- Props: `class: string = ""`, `title: string = "Timeline"`
- Slots: `default`
- Events: None
+384
View File
@@ -550,6 +550,12 @@
"category": "content",
"purpose": "Reusable accordion component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "title",
"type": "string",
@@ -1038,6 +1044,12 @@
"category": "feedback",
"purpose": "Reusable alert component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "title",
"type": "string",
@@ -1291,6 +1303,12 @@
"category": "core",
"purpose": "Reusable announcement bar component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "badge",
"type": "string",
@@ -1462,6 +1480,12 @@
"category": "content",
"purpose": "Reusable api endpoint card component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "method",
"type": "string",
@@ -3250,6 +3274,12 @@
"category": "content",
"purpose": "Reusable avatar component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "src",
"type": "string",
@@ -3456,6 +3486,12 @@
"category": "core",
"purpose": "Reusable back to top component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "label",
"type": "string",
@@ -3485,6 +3521,12 @@
"category": "feedback",
"purpose": "Reusable badge component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "label",
"type": "string",
@@ -3567,6 +3609,12 @@
"category": "feedback",
"purpose": "Reusable banner component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "text",
"type": "string",
@@ -4451,6 +4499,12 @@
"category": "core",
"purpose": "Reusable breadcrumbs component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "homeLabel",
"type": "string",
@@ -6871,6 +6925,12 @@
"category": "core",
"purpose": "Reusable checkbox component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "id",
"type": "string",
@@ -7530,6 +7590,12 @@
"category": "content",
"purpose": "Reusable code block component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "language",
"type": "string",
@@ -9932,6 +9998,12 @@
"category": "feedback",
"purpose": "Reusable cookie banner component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "title",
"type": "string",
@@ -11242,6 +11314,12 @@
"category": "layout",
"purpose": "Reusable c t a section component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "eyebrow",
"type": "string",
@@ -11826,6 +11904,12 @@
"category": "forms",
"purpose": "Reusable date input component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "id",
"type": "string",
@@ -11903,6 +11987,12 @@
"category": "forms",
"purpose": "Reusable date picker component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "id",
"type": "string",
@@ -12015,6 +12105,12 @@
"category": "forms",
"purpose": "Reusable date range picker component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "label",
"type": "string",
@@ -12103,6 +12199,12 @@
"category": "forms",
"purpose": "Reusable date time input component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "id",
"type": "string",
@@ -13934,6 +14036,12 @@
"category": "core",
"purpose": "Reusable drawer component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "title",
"type": "string",
@@ -13963,6 +14071,12 @@
"category": "overlays",
"purpose": "Reusable dropdown menu component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "label",
"type": "string",
@@ -14405,6 +14519,12 @@
"category": "forms",
"purpose": "Reusable email input component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "id",
"type": "string",
@@ -14559,6 +14679,12 @@
"category": "feedback",
"purpose": "Reusable empty state component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "icon",
"type": "string",
@@ -15271,6 +15397,12 @@
"category": "feedback",
"purpose": "Reusable error state component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "code",
"type": "string",
@@ -15883,6 +16015,12 @@
"category": "core",
"purpose": "Reusable faq component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "eyebrow",
"type": "string",
@@ -15936,6 +16074,12 @@
"category": "core",
"purpose": "Reusable faqaccordion component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "eyebrow",
"type": "string",
@@ -16155,6 +16299,12 @@
"category": "content",
"purpose": "Reusable feature card component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "icon",
"type": "string",
@@ -17067,6 +17217,12 @@
"category": "content",
"purpose": "Reusable file upload component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "id",
"type": "string",
@@ -18156,6 +18312,12 @@
"category": "content",
"purpose": "Reusable form error summary component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "title",
"type": "string",
@@ -18179,6 +18341,12 @@
"category": "forms",
"purpose": "Reusable form field component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "label",
"type": "string",
@@ -19495,6 +19663,12 @@
"category": "layout",
"purpose": "Reusable hero component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "eyebrow",
"type": "string",
@@ -20646,6 +20820,12 @@
"category": "actions",
"purpose": "Reusable icon button component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "label",
"type": "string",
@@ -23421,6 +23601,12 @@
"category": "layout",
"purpose": "Reusable legal document layout component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "title",
"type": "string",
@@ -24716,6 +24902,12 @@
"category": "core",
"purpose": "Reusable logo cloud component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "title",
"type": "string",
@@ -26624,6 +26816,12 @@
"category": "core",
"purpose": "Reusable modal component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "title",
"type": "string",
@@ -27825,6 +28023,12 @@
"category": "forms",
"purpose": "Reusable number input component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "id",
"type": "string",
@@ -28432,6 +28636,12 @@
"category": "forms",
"purpose": "Reusable otp input component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "name",
"type": "string",
@@ -28695,6 +28905,12 @@
"category": "core",
"purpose": "Reusable pagination component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "page",
"type": "number",
@@ -28953,6 +29169,12 @@
"category": "forms",
"purpose": "Reusable password input component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "id",
"type": "string",
@@ -29544,6 +29766,12 @@
"category": "forms",
"purpose": "Reusable phone input component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "id",
"type": "string",
@@ -31236,6 +31464,12 @@
"category": "content",
"purpose": "Reusable pricing plan card component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "name",
"type": "string",
@@ -32511,6 +32745,12 @@
"category": "core",
"purpose": "Reusable progress bar component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "value",
"type": "number",
@@ -33065,6 +33305,12 @@
"category": "core",
"purpose": "Reusable public footer component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "brand",
"type": "string",
@@ -33094,6 +33340,12 @@
"category": "layout",
"purpose": "Reusable public header component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "brand",
"type": "string",
@@ -33731,6 +33983,12 @@
"category": "core",
"purpose": "Reusable radio component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "id",
"type": "string",
@@ -33784,6 +34042,12 @@
"category": "forms",
"purpose": "Reusable radio group component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "label",
"type": "string",
@@ -36775,6 +37039,12 @@
"category": "forms",
"purpose": "Reusable schedule picker component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "label",
"type": "string",
@@ -37274,6 +37544,12 @@
"category": "forms",
"purpose": "Reusable search input component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "id",
"type": "string",
@@ -37828,6 +38104,12 @@
"category": "layout",
"purpose": "Reusable section header component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "eyebrow",
"type": "string",
@@ -38239,6 +38521,12 @@
"category": "core",
"purpose": "Reusable select component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "id",
"type": "string",
@@ -40009,6 +40297,12 @@
"category": "core",
"purpose": "Reusable spinner component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "label",
"type": "string",
@@ -40295,6 +40589,12 @@
"category": "content",
"purpose": "Reusable stat card component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "label",
"type": "string",
@@ -40407,6 +40707,12 @@
"category": "feedback",
"purpose": "Reusable status badge component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "status",
"type": "string",
@@ -41430,6 +41736,12 @@
"category": "core",
"purpose": "Reusable switch component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "id",
"type": "string",
@@ -41477,6 +41789,12 @@
"category": "data",
"purpose": "Reusable table component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "caption",
"type": "string",
@@ -41629,6 +41947,12 @@
"category": "content",
"purpose": "Reusable tabs component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "active",
"type": "string",
@@ -42342,6 +42666,12 @@
"category": "content",
"purpose": "Reusable testimonial card component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "quote",
"type": "string",
@@ -42483,6 +42813,12 @@
"category": "core",
"purpose": "Reusable textarea component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "id",
"type": "string",
@@ -42554,6 +42890,12 @@
"category": "forms",
"purpose": "Reusable text input component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "id",
"type": "string",
@@ -42884,6 +43226,12 @@
"category": "forms",
"purpose": "Reusable time input component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "id",
"type": "string",
@@ -42961,6 +43309,12 @@
"category": "visualization",
"purpose": "Reusable timeline component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "title",
"type": "string",
@@ -43025,6 +43379,12 @@
"category": "content",
"purpose": "Reusable timeline item component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "title",
"type": "string",
@@ -43362,6 +43722,12 @@
"category": "forms",
"purpose": "Reusable timezone selector component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "id",
"type": "string",
@@ -43391,6 +43757,12 @@
"category": "core",
"purpose": "Reusable toast component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "title",
"type": "string",
@@ -43810,6 +44182,12 @@
"category": "content",
"purpose": "Reusable tooltip component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "text",
"type": "string",
@@ -44580,6 +44958,12 @@
"category": "forms",
"purpose": "Reusable url input component.",
"props": [
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\""
},
{
"name": "id",
"type": "string",
+2 -1
View File
@@ -1,5 +1,6 @@
component Accordion {
props {
class = ""
title = "Question"
open = false
}
@@ -7,7 +8,7 @@ component Accordion {
state expanded = open
view {
<div class="border-b border-slate-200 dark:border-slate-800" >
<div class="border-b border-slate-200 dark:border-slate-800 {class}" >
<button
type="button"
class="flex w-full items-center justify-between gap-4 py-5 text-left font-semibold"
+2 -1
View File
@@ -1,5 +1,6 @@
component AnnouncementBar {
props {
class = ""
badge = "New"
message = "WrNexus Organizations is now available."
description = "Build secure multi-tenant applications with teams, roles, domains, and enterprise SSO."
@@ -14,7 +15,7 @@ component AnnouncementBar {
<aside
role="region"
aria-label="{ariaLabel}"
class="relative z-[70] overflow-hidden border-b border-indigo-200 bg-indigo-50 text-slate-900 dark:border-indigo-500/20 dark:bg-indigo-500/10 dark:text-white"
class="relative z-[70] overflow-hidden border-b border-indigo-200 bg-indigo-50 text-slate-900 dark:border-indigo-500/20 dark:bg-indigo-500/10 dark:text-white {class}"
class:hidden="!visible"
>
<!-- Decorative background -->
+2 -1
View File
@@ -1,5 +1,6 @@
component ApiEndpointCard {
props {
class = ""
method = "GET"
path = "/api/example"
description = ""
@@ -7,7 +8,7 @@ component ApiEndpointCard {
view {
<article
class="rounded-2xl border border-slate-200 bg-white p-5 dark:border-slate-800 dark:bg-slate-900"
class="rounded-2xl border border-slate-200 bg-white p-5 dark:border-slate-800 dark:bg-slate-900 {class}"
>
<div class="flex flex-wrap items-center gap-3" >
<span
+2 -1
View File
@@ -1,5 +1,6 @@
component BackToTop {
props {
class = ""
label = "Back to top"
assistiveLabel = "Return to the top of the page"
threshold = 500
@@ -49,7 +50,7 @@ component BackToTop {
view {
<div
class="group fixed bottom-6 right-5 z-[70] flex items-center gap-2 transition-all duration-300 sm:right-6 lg:bottom-8 lg:right-8"
class="group fixed bottom-6 right-5 z-[70] flex items-center gap-2 transition-all duration-300 sm:right-6 lg:bottom-8 lg:right-8 {class}"
class:pointer-events-none="!visible"
class:translate-y-5="!visible"
class:scale-90="!visible"
+2 -1
View File
@@ -1,11 +1,12 @@
component Banner {
props {
class = ""
text = "Announcement"
href = ""
actionLabel = "Learn more"
variant = "brand"
}
view {
<aside class="px-4 py-3 text-center text-sm font-medium {variant === 'warning' ? 'bg-amber-400 text-amber-950' : 'bg-violet-600 text-white'}"><span>{text}</span><a data-show="href" href="{href}" class="ml-2 underline underline-offset-4">{actionLabel} →</a></aside>
<aside class="px-4 py-3 text-center text-sm font-medium {variant === 'warning' ? 'bg-amber-400 text-amber-950' : 'bg-violet-600 text-white'} {class}"><span>{text}</span><a data-show="href" href="{href}" class="ml-2 underline underline-offset-4">{actionLabel} →</a></aside>
}
}
+2 -1
View File
@@ -1,10 +1,11 @@
component Breadcrumbs {
props {
class = ""
homeLabel = "Home"
homeHref = "/"
current = "Current page"
}
view {
<nav 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>
<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>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component CTASection {
props {
class = ""
eyebrow = "Ready?"
title = "Start building today"
description = ""
@@ -9,6 +10,6 @@ component CTASection {
secondaryHref = "#"
}
view {
<section class="overflow-hidden rounded-3xl bg-gradient-to-br from-violet-600 via-violet-700 to-fuchsia-700 px-6 py-12 text-center text-white shadow-2xl sm:px-12 sm:py-16"><p class="text-sm font-bold uppercase tracking-[0.18em] text-violet-100">{eyebrow}</p><h2 class="mx-auto mt-3 max-w-3xl text-3xl font-black sm:text-4xl">{title}</h2><p data-show="description" class="mx-auto mt-4 max-w-2xl text-violet-100">{description}</p><div class="mt-8 flex flex-wrap justify-center gap-3"><a href="{primaryHref}" class="inline-flex min-h-12 items-center rounded-xl bg-white px-5 font-semibold text-violet-700 shadow-lg transition hover:-translate-y-0.5">{primaryLabel}</a><a data-show="secondaryLabel" href="{secondaryHref}" class="inline-flex min-h-12 items-center rounded-xl border border-white/30 px-5 font-semibold text-white transition hover:bg-white/10">{secondaryLabel}</a></div></section>
<section class="overflow-hidden rounded-3xl bg-gradient-to-br from-violet-600 via-violet-700 to-fuchsia-700 text-center text-white shadow-2xl {class}"><p class="text-sm font-bold uppercase tracking-[0.18em] text-violet-100">{eyebrow}</p><h2 class="mx-auto mt-3 max-w-3xl text-3xl font-black sm:text-4xl">{title}</h2><p data-show="description" class="mx-auto mt-4 max-w-2xl text-violet-100">{description}</p><div class="mt-8 flex flex-wrap justify-center gap-3"><a href="{primaryHref}" class="inline-flex min-h-12 items-center rounded-xl bg-white px-5 font-semibold text-violet-700 shadow-lg transition hover:-translate-y-0.5">{primaryLabel}</a><a data-show="secondaryLabel" href="{secondaryHref}" class="inline-flex min-h-12 items-center rounded-xl border border-white/30 px-5 font-semibold text-white transition hover:bg-white/10">{secondaryLabel}</a></div></section>
}
}
+2 -1
View File
@@ -1,11 +1,12 @@
component CodeBlock {
props {
class = ""
language = "text"
filename = ""
code = ""
}
state copied = false
view {
<div class="overflow-hidden rounded-2xl border border-slate-800 bg-slate-950 text-slate-100"><div class="flex min-h-11 items-center justify-between border-b border-slate-800 px-4 text-xs text-slate-400"><span>{filename || language}</span><button type="button" @click="navigator.clipboard.writeText(code); copied = true" class="rounded-lg px-2 py-1 hover:bg-slate-800">{copied ? 'Copied' : 'Copy'}</button></div><pre class="overflow-x-auto p-4 text-sm leading-6"><code>{code}</code></pre></div>
<div class="overflow-hidden rounded-2xl border border-slate-800 bg-slate-950 text-slate-100 {class}"><div class="flex min-h-11 items-center justify-between border-b border-slate-800 px-4 text-xs text-slate-400"><span>{filename || language}</span><button type="button" @click="navigator.clipboard.writeText(code); copied = true" class="rounded-lg px-2 py-1 hover:bg-slate-800">{copied ? 'Copied' : 'Copy'}</button></div><pre class="overflow-x-auto p-4 text-sm leading-6"><code>{code}</code></pre></div>
}
}
+2 -1
View File
@@ -1,11 +1,12 @@
component CookieBanner {
props {
class = ""
title = "We use cookies"
description = "We use essential cookies and optional analytics to improve your experience."
privacyHref = "/privacy"
}
state visible = true
view {
<aside data-show="visible" class="fixed inset-x-4 bottom-4 z-50 mx-auto max-w-4xl rounded-3xl border border-slate-200 bg-white/95 p-5 shadow-2xl backdrop-blur-xl dark:border-slate-800 dark:bg-slate-900/95" role="dialog" aria-label="Cookie preferences"><div class="flex flex-col gap-5 md:flex-row md:items-center"><div class="min-w-0 flex-1"><h2 class="font-bold">{title}</h2><p class="mt-1 text-sm text-slate-500">{description} <a href="{privacyHref}" class="text-violet-600 underline">Privacy policy</a></p></div><div class="flex flex-wrap gap-2"><button @click="visible = false" class="min-h-11 rounded-xl border border-slate-300 px-4 text-sm font-semibold dark:border-slate-700">Reject optional</button><button @click="visible = false" class="min-h-11 rounded-xl bg-violet-600 px-4 text-sm font-semibold text-white">Accept all</button></div></div></aside>
<aside data-show="visible" class="fixed inset-x-4 bottom-4 z-50 max-w-4xl rounded-3xl border border-slate-200 bg-white/95 p-5 shadow-2xl backdrop-blur-xl dark:border-slate-800 dark:bg-slate-900/95 {class}" role="dialog" aria-label="Cookie preferences"><div class="flex flex-col gap-5 md:flex-row md:items-center"><div class="min-w-0 flex-1"><h2 class="font-bold">{title}</h2><p class="mt-1 text-sm text-slate-500">{description} <a href="{privacyHref}" class="text-violet-600 underline">Privacy policy</a></p></div><div class="flex flex-wrap gap-2"><button @click="visible = false" class="min-h-11 rounded-xl border border-slate-300 px-4 text-sm font-semibold dark:border-slate-700">Reject optional</button><button @click="visible = false" class="min-h-11 rounded-xl bg-violet-600 px-4 text-sm font-semibold text-white">Accept all</button></div></div></aside>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component DateInput {
props {
class = ""
id: string = ""
name: string = ""
label: string = "Date"
@@ -13,7 +14,7 @@ component DateInput {
autocomplete: string = ""
}
view {
<div class="space-y-2">
<div class="space-y-2 {class}">
<label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label>
<input id="{id}" name="{name}" type="date" value="{value}" placeholder="{placeholder}" required="{required}" disabled="{disabled}" readonly="{readonly}" autocomplete="{autocomplete}" aria-invalid="{!!error}" aria-describedby="{id}-help {id}-error" class="min-h-11 w-full rounded-xl border bg-white px-3.5 py-2.5 text-slate-950 shadow-sm outline-none transition duration-200 placeholder:text-slate-400 focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 disabled:cursor-not-allowed disabled:bg-slate-100 disabled:opacity-70 dark:bg-slate-950 dark:text-white {error ? 'border-red-500 focus:border-red-500 focus:ring-red-500/10' : 'border-slate-300 dark:border-slate-700'}" />
<p id="{id}-help" data-show="help && !error" class="text-sm text-slate-500 dark:text-slate-400">{help}</p>
+2 -1
View File
@@ -1,5 +1,6 @@
component DatePicker {
props {
class = ""
id: string = "date"
name: string = "date"
label: string = "Date"
@@ -9,6 +10,6 @@ component DatePicker {
required: boolean = false
}
view {
<DateInput id="{id}" name="{name}" label="{label}" value="{value}" required="{required}" />
<DateInput class="{class}" id="{id}" name="{name}" label="{label}" value="{value}" required="{required}" />
}
}
+2 -1
View File
@@ -1,10 +1,11 @@
component DateRangePicker {
props {
class = ""
label = "Date range"
startName = "startDate"
endName = "endDate"
}
view {
<fieldset><legend class="mb-2 text-sm font-semibold">{label}</legend><div class="grid gap-3 sm:grid-cols-2"><DateInput id="{startName}" name="{startName}" label="Start date" /><DateInput id="{endName}" name="{endName}" label="End date" /></div></fieldset>
<fieldset class="{class}"><legend class="mb-2 text-sm font-semibold">{label}</legend><div class="grid gap-3 sm:grid-cols-2"><DateInput id="{startName}" name="{startName}" label="Start date" /><DateInput id="{endName}" name="{endName}" label="End date" /></div></fieldset>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component DateTimeInput {
props {
class = ""
id = ""
name = ""
label = "DateTime"
@@ -13,7 +14,7 @@ component DateTimeInput {
autocomplete = ""
}
view {
<div class="space-y-2">
<div class="space-y-2 {class}">
<label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label>
<input id="{id}" name="{name}" type="datetime-local" value="{value}" placeholder="{placeholder}" required="{required}" disabled="{disabled}" readonly="{readonly}" autocomplete="{autocomplete}" aria-invalid="{!!error}" aria-describedby="{id}-help {id}-error" class="min-h-11 w-full rounded-xl border bg-white px-3.5 py-2.5 text-slate-950 shadow-sm outline-none transition duration-200 placeholder:text-slate-400 focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 disabled:cursor-not-allowed disabled:bg-slate-100 disabled:opacity-70 dark:bg-slate-950 dark:text-white {error ? 'border-red-500 focus:border-red-500 focus:ring-red-500/10' : 'border-slate-300 dark:border-slate-700'}" />
<p id="{id}-help" data-show="help && !error" class="text-sm text-slate-500 dark:text-slate-400">{help}</p>
+2 -1
View File
@@ -1,11 +1,12 @@
component Drawer {
props {
class = ""
title = "Panel"
open = false
side = "right"
}
state visible = open
view {
<div data-show="visible" class="fixed inset-0 z-50" role="dialog" aria-modal="true"><button class="absolute inset-0 bg-slate-950/50" aria-label="Close" @click="visible = false"></button><aside class="absolute inset-y-0 w-[min(92vw,28rem)] overflow-auto bg-white p-6 shadow-2xl transition dark:bg-slate-900 {side === 'left' ? 'left-0' : 'right-0'}"><header class="flex items-center justify-between"><h2 class="text-xl font-bold">{title}</h2><button @click="visible = false" class="size-10 rounded-xl hover:bg-slate-100">×</button></header><div class="mt-6"><slot /></div></aside></div>
<div data-show="visible" class="fixed inset-0 z-50 {class}" role="dialog" aria-modal="true"><button class="absolute inset-0 bg-slate-950/50" aria-label="Close" @click="visible = false"></button><aside class="absolute inset-y-0 w-[min(92vw,28rem)] overflow-auto bg-white p-6 shadow-2xl transition dark:bg-slate-900 {side === 'left' ? 'left-0' : 'right-0'}"><header class="flex items-center justify-between"><h2 class="text-xl font-bold">{title}</h2><button @click="visible = false" class="size-10 rounded-xl hover:bg-slate-100">×</button></header><div class="mt-6"><slot /></div></aside></div>
}
}
+2 -1
View File
@@ -1,9 +1,10 @@
component DropdownMenu {
props {
class = ""
label = "Menu"
}
state open = false
view {
<div class="relative inline-block"><button type="button" aria-haspopup="menu" aria-expanded="{open}" @click="open = !open" class="inline-flex min-h-11 items-center gap-2 rounded-xl border border-slate-300 px-4 text-sm font-semibold dark:border-slate-700">{label}<span aria-hidden="true">⌄</span></button><div data-show="open" role="menu" class="absolute right-0 z-30 mt-2 min-w-48 rounded-xl border border-slate-200 bg-white p-1.5 shadow-xl dark:border-slate-800 dark:bg-slate-900"><slot /></div></div>
<div class="relative inline-block {class}"><button type="button" aria-haspopup="menu" aria-expanded="{open}" @click="open = !open" class="inline-flex min-h-11 items-center gap-2 rounded-xl border border-slate-300 px-4 text-sm font-semibold dark:border-slate-700">{label}<span aria-hidden="true">⌄</span></button><div data-show="open" role="menu" class="absolute right-0 z-30 mt-2 min-w-48 rounded-xl border border-slate-200 bg-white p-1.5 shadow-xl dark:border-slate-800 dark:bg-slate-900"><slot /></div></div>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component EmailInput {
props {
class = ""
id = ""
name = ""
label = "Email"
@@ -13,7 +14,7 @@ component EmailInput {
autocomplete = ""
}
view {
<div class="space-y-2">
<div class="space-y-2 {class}">
<label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label>
<input id="{id}" name="{name}" type="email" value="{value}" placeholder="{placeholder}" required="{required}" disabled="{disabled}" readonly="{readonly}" autocomplete="{autocomplete}" aria-invalid="{!!error}" aria-describedby="{id}-help {id}-error" class="min-h-11 w-full rounded-xl border bg-white px-3.5 py-2.5 text-slate-950 shadow-sm outline-none transition duration-200 placeholder:text-slate-400 focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 disabled:cursor-not-allowed disabled:bg-slate-100 disabled:opacity-70 dark:bg-slate-950 dark:text-white {error ? 'border-red-500 focus:border-red-500 focus:ring-red-500/10' : 'border-slate-300 dark:border-slate-700'}" />
<p id="{id}-help" data-show="help && !error" class="text-sm text-slate-500 dark:text-slate-400">{help}</p>
+2 -1
View File
@@ -1,5 +1,6 @@
component EmptyState {
props {
class = ""
icon = "◇"
title = "Nothing here yet"
description = ""
@@ -7,6 +8,6 @@ component EmptyState {
actionHref = "#"
}
view {
<div class="rounded-3xl border border-dashed border-slate-300 px-6 py-14 text-center dark:border-slate-700"><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>
<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>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component ErrorState {
props {
class = ""
code = "500"
title = "Something went wrong"
description = "Please try again."
@@ -7,6 +8,6 @@ component ErrorState {
actionHref = "/"
}
view {
<div class="mx-auto max-w-2xl px-4 py-24 text-center"><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>
<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>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component FAQ {
props {
class = ""
eyebrow = ""
title = "Frequently asked questions"
description = ""
@@ -8,5 +9,5 @@ component FAQ {
allowMultiple = false
defaultOpenIndex = 0
}
view { <FAQAccordion eyebrow="{eyebrow}" title="{title}" description="{description}" items="{items}" centered="{centered}" allowMultiple="{allowMultiple}" defaultOpenIndex="{defaultOpenIndex}" /> }
view { <FAQAccordion class="{class}" eyebrow="{eyebrow}" title="{title}" description="{description}" items="{items}" centered="{centered}" allowMultiple="{allowMultiple}" defaultOpenIndex="{defaultOpenIndex}" /> }
}
+2 -1
View File
@@ -1,5 +1,6 @@
component FAQAccordion {
props {
class = ""
eyebrow = ""
title = "Frequently asked questions"
description = ""
@@ -41,7 +42,7 @@ component FAQAccordion {
view {
<section
aria-labelledby="faq-accordion-title"
class="w-full px-5 sm:px-8 lg:px-10 xl:px-12"
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"
+2 -1
View File
@@ -1,11 +1,12 @@
component FeatureCard {
props {
class = ""
icon = "✦"
title = "Feature"
description = ""
href = ""
}
view {
<article class="group rounded-2xl border border-slate-200 bg-white p-6 transition duration-300 hover:-translate-y-1 hover:border-violet-300 hover:shadow-xl dark:border-slate-800 dark:bg-slate-900"><div class="flex size-11 items-center justify-center rounded-xl bg-violet-100 text-xl text-violet-700 transition group-hover:scale-110 dark:bg-violet-950 dark:text-violet-300">{icon}</div><h3 class="mt-5 text-lg font-bold">{title}</h3><p class="mt-2 leading-7 text-slate-600 dark:text-slate-300">{description}</p><a data-show="href" href="{href}" class="mt-4 inline-flex font-semibold text-violet-600">Learn more →</a></article>
<article class="group rounded-2xl border border-slate-200 bg-white p-6 transition duration-300 hover:-translate-y-1 hover:border-violet-300 hover:shadow-xl dark:border-slate-800 dark:bg-slate-900 {class}"><div class="flex size-11 items-center justify-center rounded-xl bg-violet-100 text-xl text-violet-700 transition group-hover:scale-110 dark:bg-violet-950 dark:text-violet-300">{icon}</div><h3 class="mt-5 text-lg font-bold">{title}</h3><p class="mt-2 leading-7 text-slate-600 dark:text-slate-300">{description}</p><a data-show="href" href="{href}" class="mt-4 inline-flex font-semibold text-violet-600">Learn more →</a></article>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component FileUpload {
props {
class = ""
id = "file"
name = "file"
label = "Upload file"
@@ -8,6 +9,6 @@ component FileUpload {
help = "Drag and drop or browse"
}
view {
<label for="{id}" class="flex min-h-40 cursor-pointer flex-col items-center justify-center rounded-2xl border-2 border-dashed border-slate-300 bg-slate-50 p-6 text-center transition hover:border-violet-500 hover:bg-violet-50/50 focus-within:ring-4 focus-within:ring-violet-500/10 dark:border-slate-700 dark:bg-slate-900/50"><span class="text-3xl" aria-hidden="true">⇧</span><span class="mt-2 font-semibold">{label}</span><span class="mt-1 text-sm text-slate-500">{help}</span><input id="{id}" name="{name}" type="file" accept="{accept}" multiple="{multiple}" class="sr-only" /></label>
<label for="{id}" class="flex min-h-40 cursor-pointer flex-col items-center justify-center rounded-2xl border-2 border-dashed border-slate-300 bg-slate-50 p-6 text-center transition hover:border-violet-500 hover:bg-violet-50/50 focus-within:ring-4 focus-within:ring-violet-500/10 dark:border-slate-700 dark:bg-slate-900/50 {class}"><span class="text-3xl" aria-hidden="true">⇧</span><span class="mt-2 font-semibold">{label}</span><span class="mt-1 text-sm text-slate-500">{help}</span><input id="{id}" name="{name}" type="file" accept="{accept}" multiple="{multiple}" class="sr-only" /></label>
}
}
+2 -1
View File
@@ -1,9 +1,10 @@
component FormErrorSummary {
props {
class = ""
title = "Please fix the following"
visible = true
}
view {
<section data-show="visible" role="alert" aria-live="polite" class="rounded-2xl border border-red-200 bg-red-50 p-4 text-red-900 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200"><h3 class="font-semibold">{title}</h3><div class="mt-2 text-sm"><slot /></div></section>
<section data-show="visible" role="alert" aria-live="polite" class="rounded-2xl border border-red-200 bg-red-50 p-4 text-red-900 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200 {class}"><h3 class="font-semibold">{title}</h3><div class="mt-2 text-sm"><slot /></div></section>
}
}
+2 -1
View File
@@ -1,11 +1,12 @@
component FormField {
props {
class = ""
label = "Field"
help = ""
error = ""
required = false
}
view {
<div class="space-y-2"><div class="text-sm font-semibold">{label}<span data-show="required" class="ml-1 text-red-500">*</span></div><slot /><p data-show="help && !error" class="text-sm text-slate-500">{help}</p><p data-show="error" role="alert" class="text-sm font-medium text-red-600">{error}</p></div>
<div class="space-y-2 {class}"><div class="text-sm font-semibold">{label}<span data-show="required" class="ml-1 text-red-500">*</span></div><slot /><p data-show="help && !error" class="text-sm text-slate-500">{help}</p><p data-show="error" role="alert" class="text-sm font-medium text-red-600">{error}</p></div>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component Hero {
props {
class = ""
eyebrow = ""
title = "Build faster with WRNexusJS"
highlight = ""
@@ -11,6 +12,6 @@ component Hero {
align = "center"
}
view {
<section class="relative isolate overflow-hidden py-20 sm:py-28"><div class="absolute inset-0 -z-10 bg-[radial-gradient(circle_at_top,rgba(124,58,237,.18),transparent_45%)]"></div><div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"><div class="max-w-4xl {align === 'center' ? 'mx-auto text-center' : ''}"><Badge data-show="eyebrow" label="{eyebrow}" variant="brand" /><h1 class="mt-6 text-4xl font-black tracking-tight text-slate-950 sm:text-6xl dark:text-white">{title} <span data-show="highlight" class="bg-gradient-to-r from-violet-600 to-fuchsia-500 bg-clip-text text-transparent">{highlight}</span></h1><p data-show="description" class="mt-6 text-lg leading-8 text-slate-600 sm:text-xl dark:text-slate-300">{description}</p><div class="mt-8 flex flex-wrap gap-3 {align === 'center' ? 'justify-center' : ''}"><a href="{primaryHref}" class="inline-flex min-h-12 items-center rounded-xl bg-violet-600 px-5 font-semibold text-white shadow-lg shadow-violet-600/20 transition hover:-translate-y-0.5 hover:bg-violet-700">{primaryLabel}</a><a data-show="secondaryLabel" href="{secondaryHref}" class="inline-flex min-h-12 items-center rounded-xl border border-slate-300 bg-white px-5 font-semibold text-slate-900 transition hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:text-white">{secondaryLabel}</a></div><div class="mt-10"><slot /></div></div></div></section>
<section class="relative isolate overflow-hidden {class}"><div class="absolute inset-0 -z-10 bg-[radial-gradient(circle_at_top,rgba(124,58,237,.18),transparent_45%)]"></div><div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"><div class="max-w-4xl {align === 'center' ? 'mx-auto text-center' : ''}"><Badge data-show="eyebrow" label="{eyebrow}" variant="brand" /><h1 class="mt-6 text-4xl font-black tracking-tight text-slate-950 sm:text-6xl dark:text-white">{title} <span data-show="highlight" class="bg-gradient-to-r from-violet-600 to-fuchsia-500 bg-clip-text text-transparent">{highlight}</span></h1><p data-show="description" class="mt-6 text-lg leading-8 text-slate-600 sm:text-xl dark:text-slate-300">{description}</p><div class="mt-8 flex flex-wrap gap-3 {align === 'center' ? 'justify-center' : ''}"><a href="{primaryHref}" class="inline-flex min-h-12 items-center rounded-xl bg-violet-600 px-5 font-semibold text-white shadow-lg shadow-violet-600/20 transition hover:-translate-y-0.5 hover:bg-violet-700">{primaryLabel}</a><a data-show="secondaryLabel" href="{secondaryHref}" class="inline-flex min-h-12 items-center rounded-xl border border-slate-300 bg-white px-5 font-semibold text-slate-900 transition hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:text-white">{secondaryLabel}</a></div><div class="mt-10"><slot /></div></div></div></section>
}
}
+2 -1
View File
@@ -1,9 +1,10 @@
component IconButton {
props {
class = ""
label = "Action"
icon = "•"
variant = "ghost"
disabled = false
}
view { <button type="button" aria-label="{label}" title="{label}" disabled="{disabled}" class="inline-flex size-11 items-center justify-center rounded-xl transition duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-500 disabled:opacity-50 {variant === 'primary' ? 'bg-violet-600 text-white hover:bg-violet-700' : 'text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-800'}"><span aria-hidden="true">{icon}</span></button> }
view { <button type="button" aria-label="{label}" title="{label}" disabled="{disabled}" class="inline-flex size-11 items-center justify-center rounded-xl transition duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-500 disabled:opacity-50 {variant === 'primary' ? 'bg-violet-600 text-white hover:bg-violet-700' : 'text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-800'} {class}"><span aria-hidden="true">{icon}</span></button> }
}
@@ -1,10 +1,11 @@
component LegalDocumentLayout {
props {
class = ""
title = "Legal document"
effectiveDate = ""
updatedDate = ""
}
view {
<article class="mx-auto grid max-w-7xl gap-10 px-4 py-14 lg:grid-cols-[16rem_minmax(0,1fr)] lg:px-8"><aside class="lg:sticky lg:top-24 lg:self-start"><div class="text-sm font-semibold">On this page</div><nav class="mt-4 text-sm text-slate-500"><slot name="toc" /></nav></aside><main class="min-w-0 max-w-3xl"><header class="border-b border-slate-200 pb-8 dark:border-slate-800"><h1 class="text-4xl font-black tracking-tight">{title}</h1><div class="mt-4 flex flex-wrap gap-4 text-sm text-slate-500"><span data-show="effectiveDate">Effective: {effectiveDate}</span><span data-show="updatedDate">Updated: {updatedDate}</span></div></header><div class="prose prose-slate mt-8 max-w-none dark:prose-invert"><slot /></div></main></article>
<article class="grid max-w-7xl gap-10 lg:grid-cols-[16rem_minmax(0,1fr)] {class}"><aside class="lg:sticky lg:top-24 lg:self-start"><div class="text-sm font-semibold">On this page</div><nav class="mt-4 text-sm text-slate-500"><slot name="toc" /></nav></aside><main class="min-w-0 max-w-3xl"><header class="border-b border-slate-200 pb-8 dark:border-slate-800"><h1 class="text-4xl font-black tracking-tight">{title}</h1><div class="mt-4 flex flex-wrap gap-4 text-sm text-slate-500"><span data-show="effectiveDate">Effective: {effectiveDate}</span><span data-show="updatedDate">Updated: {updatedDate}</span></div></header><div class="prose prose-slate mt-8 max-w-none dark:prose-invert"><slot /></div></main></article>
}
}
+2 -1
View File
@@ -1,8 +1,9 @@
component LogoCloud {
props {
class = ""
title = "Trusted by modern teams"
}
view {
<section 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>
<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>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component Modal {
props {
class = ""
title = "Dialog"
description = ""
open = false
@@ -8,6 +9,6 @@ component Modal {
}
state visible = open
view {
<div data-show="visible" class="fixed inset-0 z-50 flex items-end justify-center p-4 sm:items-center" role="dialog" aria-modal="true" aria-labelledby="modal-title"><button class="absolute inset-0 bg-slate-950/60 backdrop-blur-sm" aria-label="{closeLabel}" @click="visible = false"></button><section class="relative max-h-[90vh] w-full overflow-auto rounded-3xl border border-slate-200 bg-white p-6 shadow-2xl dark:border-slate-800 dark:bg-slate-900 {size === 'sm' ? 'max-w-md' : size === 'lg' ? 'max-w-4xl' : 'max-w-2xl'}"><header class="flex items-start justify-between gap-4"><div><h2 id="modal-title" class="text-xl font-bold">{title}</h2><p data-show="description" class="mt-1 text-sm text-slate-500">{description}</p></div><button type="button" aria-label="{closeLabel}" @click="visible = false" class="inline-flex size-10 items-center justify-center rounded-xl hover:bg-slate-100 dark:hover:bg-slate-800">×</button></header><div class="mt-5"><slot /></div></section></div>
<div data-show="visible" class="fixed inset-0 z-50 flex items-end justify-center p-4 sm:items-center {class}" role="dialog" aria-modal="true" aria-labelledby="modal-title"><button class="absolute inset-0 bg-slate-950/60 backdrop-blur-sm" aria-label="{closeLabel}" @click="visible = false"></button><section class="relative max-h-[90vh] w-full overflow-auto rounded-3xl border border-slate-200 bg-white p-6 shadow-2xl dark:border-slate-800 dark:bg-slate-900 {size === 'sm' ? 'max-w-md' : size === 'lg' ? 'max-w-4xl' : 'max-w-2xl'}"><header class="flex items-start justify-between gap-4"><div><h2 id="modal-title" class="text-xl font-bold">{title}</h2><p data-show="description" class="mt-1 text-sm text-slate-500">{description}</p></div><button type="button" aria-label="{closeLabel}" @click="visible = false" class="inline-flex size-10 items-center justify-center rounded-xl hover:bg-slate-100 dark:hover:bg-slate-800">×</button></header><div class="mt-5"><slot /></div></section></div>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component NumberInput {
props {
class = ""
id = ""
name = ""
label = "Number"
@@ -13,7 +14,7 @@ component NumberInput {
autocomplete = ""
}
view {
<div class="space-y-2">
<div class="space-y-2 {class}">
<label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label>
<input id="{id}" name="{name}" type="number" value="{value}" placeholder="{placeholder}" required="{required}" disabled="{disabled}" readonly="{readonly}" autocomplete="{autocomplete}" aria-invalid="{!!error}" aria-describedby="{id}-help {id}-error" class="min-h-11 w-full rounded-xl border bg-white px-3.5 py-2.5 text-slate-950 shadow-sm outline-none transition duration-200 placeholder:text-slate-400 focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 disabled:cursor-not-allowed disabled:bg-slate-100 disabled:opacity-70 dark:bg-slate-950 dark:text-white {error ? 'border-red-500 focus:border-red-500 focus:ring-red-500/10' : 'border-slate-300 dark:border-slate-700'}" />
<p id="{id}-help" data-show="help && !error" class="text-sm text-slate-500 dark:text-slate-400">{help}</p>
+2 -1
View File
@@ -1,10 +1,11 @@
component OtpInput {
props {
class = ""
name = "otp"
length = 6
label = "Verification code"
}
view {
<fieldset><legend class="mb-3 text-sm font-semibold">{label}</legend><input name="{name}" inputmode="numeric" autocomplete="one-time-code" maxlength="{length}" pattern="[0-9]*" class="min-h-14 w-full rounded-2xl border border-slate-300 bg-white px-4 text-center font-mono text-2xl tracking-[0.5em] outline-none transition focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 dark:border-slate-700 dark:bg-slate-950" /></fieldset>
<fieldset class="{class}"><legend class="mb-3 text-sm font-semibold">{label}</legend><input name="{name}" inputmode="numeric" autocomplete="one-time-code" maxlength="{length}" pattern="[0-9]*" class="min-h-14 w-full rounded-2xl border border-slate-300 bg-white px-4 text-center font-mono text-2xl tracking-[0.5em] outline-none transition focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 dark:border-slate-700 dark:bg-slate-950" /></fieldset>
}
}
+2 -1
View File
@@ -1,11 +1,12 @@
component Pagination {
props {
class = ""
page = 1
totalPages = 1
previousHref = "#"
nextHref = "#"
}
view {
<nav aria-label="Pagination" class="flex items-center justify-between gap-4"><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>
<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>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component PasswordInput {
props {
class = ""
id = ""
name = ""
label = "Password"
@@ -13,7 +14,7 @@ component PasswordInput {
autocomplete = ""
}
view {
<div class="space-y-2">
<div class="space-y-2 {class}">
<label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label>
<input id="{id}" name="{name}" type="password" value="{value}" placeholder="{placeholder}" required="{required}" disabled="{disabled}" readonly="{readonly}" autocomplete="{autocomplete}" aria-invalid="{!!error}" aria-describedby="{id}-help {id}-error" class="min-h-11 w-full rounded-xl border bg-white px-3.5 py-2.5 text-slate-950 shadow-sm outline-none transition duration-200 placeholder:text-slate-400 focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 disabled:cursor-not-allowed disabled:bg-slate-100 disabled:opacity-70 dark:bg-slate-950 dark:text-white {error ? 'border-red-500 focus:border-red-500 focus:ring-red-500/10' : 'border-slate-300 dark:border-slate-700'}" />
<p id="{id}-help" data-show="help && !error" class="text-sm text-slate-500 dark:text-slate-400">{help}</p>
+2 -1
View File
@@ -1,5 +1,6 @@
component PhoneInput {
props {
class = ""
id = ""
name = ""
label = "Phone"
@@ -13,7 +14,7 @@ component PhoneInput {
autocomplete = ""
}
view {
<div class="space-y-2">
<div class="space-y-2 {class}">
<label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label>
<input id="{id}" name="{name}" type="tel" value="{value}" placeholder="{placeholder}" required="{required}" disabled="{disabled}" readonly="{readonly}" autocomplete="{autocomplete}" aria-invalid="{!!error}" aria-describedby="{id}-help {id}-error" class="min-h-11 w-full rounded-xl border bg-white px-3.5 py-2.5 text-slate-950 shadow-sm outline-none transition duration-200 placeholder:text-slate-400 focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 disabled:cursor-not-allowed disabled:bg-slate-100 disabled:opacity-70 dark:bg-slate-950 dark:text-white {error ? 'border-red-500 focus:border-red-500 focus:ring-red-500/10' : 'border-slate-300 dark:border-slate-700'}" />
<p id="{id}-help" data-show="help && !error" class="text-sm text-slate-500 dark:text-slate-400">{help}</p>
+2 -1
View File
@@ -1,5 +1,6 @@
component PricingPlanCard {
props {
class = ""
name = "Starter"
description = ""
price = "₹0"
@@ -9,6 +10,6 @@ component PricingPlanCard {
ctaHref = "#"
}
view {
<article class="relative flex h-full flex-col rounded-3xl border p-6 shadow-sm {featured ? 'border-violet-500 bg-violet-50/50 ring-2 ring-violet-500 dark:bg-violet-950/20' : 'border-slate-200 bg-white dark:border-slate-800 dark:bg-slate-900'}"><Badge data-show="featured" label="Most popular" variant="brand" /><h3 class="mt-4 text-xl font-bold">{name}</h3><p class="mt-2 text-sm text-slate-500">{description}</p><div class="mt-6"><span class="text-4xl font-black">{price}</span><span class="text-slate-500">{period}</span></div><div class="mt-6 flex-1"><slot /></div><a href="{ctaHref}" class="mt-6 inline-flex min-h-12 items-center justify-center rounded-xl font-semibold transition {featured ? 'bg-violet-600 text-white hover:bg-violet-700' : 'border border-slate-300 hover:bg-slate-50 dark:border-slate-700'}">{ctaLabel}</a></article>
<article class="relative flex h-full flex-col rounded-3xl border p-6 shadow-sm {featured ? 'border-violet-500 bg-violet-50/50 ring-2 ring-violet-500 dark:bg-violet-950/20' : 'border-slate-200 bg-white dark:border-slate-800 dark:bg-slate-900'} {class}"><Badge data-show="featured" label="Most popular" variant="brand" /><h3 class="mt-4 text-xl font-bold">{name}</h3><p class="mt-2 text-sm text-slate-500">{description}</p><div class="mt-6"><span class="text-4xl font-black">{price}</span><span class="text-slate-500">{period}</span></div><div class="mt-6 flex-1"><slot /></div><a href="{ctaHref}" class="mt-6 inline-flex min-h-12 items-center justify-center rounded-xl font-semibold transition {featured ? 'bg-violet-600 text-white hover:bg-violet-700' : 'border border-slate-300 hover:bg-slate-50 dark:border-slate-700'}">{ctaLabel}</a></article>
}
}
+2 -1
View File
@@ -1,11 +1,12 @@
component ProgressBar {
props {
class = ""
value = 0
max = 100
label = "Progress"
showValue = true
}
view {
<div><div class="mb-2 flex justify-between text-sm"><span>{label}</span><span data-show="showValue">{value}%</span></div><div class="h-2 overflow-hidden rounded-full bg-slate-200 dark:bg-slate-800" role="progressbar" aria-valuenow="{value}" aria-valuemin="0" aria-valuemax="{max}"><div class="h-full rounded-full bg-violet-600 transition-all duration-500" style="width: {value}%"></div></div></div>
<div class="{class}"><div class="mb-2 flex justify-between text-sm"><span>{label}</span><span data-show="showValue">{value}%</span></div><div class="h-2 overflow-hidden rounded-full bg-slate-200 dark:bg-slate-800" role="progressbar" aria-valuenow="{value}" aria-valuemin="0" aria-valuemax="{max}"><div class="h-full rounded-full bg-violet-600 transition-all duration-500" style="width: {value}%"></div></div></div>
}
}
+2 -1
View File
@@ -1,10 +1,11 @@
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"><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>
<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>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component PublicHeader {
props {
class = ""
brand = "WRNexusJS"
homeHref = "/"
signInHref = "/signin"
@@ -8,6 +9,6 @@ component PublicHeader {
}
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"><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>
<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>
}
}
+2 -1
View File
@@ -1,9 +1,10 @@
component RadioGroup {
props {
class = ""
label = "Choose one"
name = "choice"
}
view {
<fieldset class="space-y-3"><legend class="text-sm font-semibold text-slate-900 dark:text-white">{label}</legend><div class="space-y-2"><slot /></div></fieldset>
<fieldset class="space-y-3 {class}"><legend class="text-sm font-semibold text-slate-900 dark:text-white">{label}</legend><div class="space-y-2"><slot /></div></fieldset>
}
}
+2 -1
View File
@@ -1,8 +1,9 @@
component SchedulePicker {
props {
class = ""
label = "Schedule"
}
view {
<fieldset class="space-y-4 rounded-2xl border border-slate-200 p-5 dark:border-slate-800"><legend class="px-2 font-semibold">{label}</legend><DateTimeInput id="scheduleAt" name="scheduleAt" label="Date and time" /><TimezoneSelector /></fieldset>
<fieldset class="space-y-4 rounded-2xl border border-slate-200 p-5 dark:border-slate-800 {class}"><legend class="px-2 font-semibold">{label}</legend><DateTimeInput id="scheduleAt" name="scheduleAt" label="Date and time" /><TimezoneSelector /></fieldset>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component SearchInput {
props {
class = ""
id = ""
name = ""
label = "Search"
@@ -13,7 +14,7 @@ component SearchInput {
autocomplete = ""
}
view {
<div class="space-y-2">
<div class="space-y-2 {class}">
<label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label>
<input id="{id}" name="{name}" type="search" value="{value}" placeholder="{placeholder}" required="{required}" disabled="{disabled}" readonly="{readonly}" autocomplete="{autocomplete}" aria-invalid="{!!error}" aria-describedby="{id}-help {id}-error" class="min-h-11 w-full rounded-xl border bg-white px-3.5 py-2.5 text-slate-950 shadow-sm outline-none transition duration-200 placeholder:text-slate-400 focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 disabled:cursor-not-allowed disabled:bg-slate-100 disabled:opacity-70 dark:bg-slate-950 dark:text-white {error ? 'border-red-500 focus:border-red-500 focus:ring-red-500/10' : 'border-slate-300 dark:border-slate-700'}" />
<p id="{id}-help" data-show="help && !error" class="text-sm text-slate-500 dark:text-slate-400">{help}</p>
+2 -1
View File
@@ -1,11 +1,12 @@
component SectionHeader {
props {
class = ""
eyebrow = ""
title = "Section title"
description = ""
align = "left"
}
view {
<header class="max-w-3xl {align === 'center' ? 'mx-auto text-center' : ''}"><p data-show="eyebrow" class="mb-3 text-sm font-bold uppercase tracking-[0.18em] text-violet-600 dark:text-violet-400">{eyebrow}</p><h2 class="text-3xl font-bold tracking-tight text-slate-950 sm:text-4xl dark:text-white">{title}</h2><p data-show="description" class="mt-4 text-lg leading-8 text-slate-600 dark:text-slate-300">{description}</p><div class="mt-6"><slot /></div></header>
<header class="max-w-3xl {align === 'center' ? 'mx-auto text-center' : ''} {class}"><p data-show="eyebrow" class="mb-3 text-sm font-bold uppercase tracking-[0.18em] text-violet-600 dark:text-violet-400">{eyebrow}</p><h2 class="text-3xl font-bold tracking-tight text-slate-950 sm:text-4xl dark:text-white">{title}</h2><p data-show="description" class="mt-4 text-lg leading-8 text-slate-600 dark:text-slate-300">{description}</p><div class="mt-6"><slot /></div></header>
}
}
+2 -1
View File
@@ -1,11 +1,12 @@
component StatCard {
props {
class = ""
label = "Metric"
value = "0"
change = ""
trend = "neutral"
}
view {
<article class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900"><p class="text-sm font-medium text-slate-500">{label}</p><div class="mt-2 flex items-end justify-between gap-3"><p class="text-3xl font-bold tracking-tight">{value}</p><span data-show="change" class="text-sm font-semibold {trend === 'up' ? 'text-emerald-600' : trend === 'down' ? 'text-red-600' : 'text-slate-500'}">{trend === 'up' ? '↑' : trend === 'down' ? '↓' : ''} {change}</span></div></article>
<article class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900 {class}"><p class="text-sm font-medium text-slate-500">{label}</p><div class="mt-2 flex items-end justify-between gap-3"><p class="text-3xl font-bold tracking-tight">{value}</p><span data-show="change" class="text-sm font-semibold {trend === 'up' ? 'text-emerald-600' : trend === 'down' ? 'text-red-600' : 'text-slate-500'}">{trend === 'up' ? '↑' : trend === 'down' ? '↓' : ''} {change}</span></div></article>
}
}
+2 -1
View File
@@ -1,8 +1,9 @@
component StatusBadge {
props {
class = ""
status = "operational"
}
view {
<span class="inline-flex items-center gap-2 rounded-full px-3 py-1 text-xs font-semibold {status === 'operational' ? 'bg-emerald-100 text-emerald-700' : status === 'degraded' ? 'bg-amber-100 text-amber-700' : status === 'outage' ? 'bg-red-100 text-red-700' : 'bg-blue-100 text-blue-700'}"><span class="size-2 rounded-full bg-current"></span>{status}</span>
<span class="inline-flex items-center gap-2 rounded-full px-3 py-1 text-xs font-semibold {status === 'operational' ? 'bg-emerald-100 text-emerald-700' : status === 'degraded' ? 'bg-amber-100 text-amber-700' : status === 'outage' ? 'bg-red-100 text-red-700' : 'bg-blue-100 text-blue-700'} {class}"><span class="size-2 rounded-full bg-current"></span>{status}</span>
}
}
+2 -1
View File
@@ -1,9 +1,10 @@
component Tabs {
props {
class = ""
active = "first"
}
state current = active
view {
<div><div role="tablist" class="flex gap-1 overflow-x-auto rounded-xl bg-slate-100 p-1 dark:bg-slate-800"><button role="tab" aria-selected="{current === 'first'}" @click="current = 'first'" class="min-h-10 flex-1 rounded-lg px-4 text-sm font-semibold transition {current === 'first' ? 'bg-white shadow dark:bg-slate-900' : 'text-slate-500'}">First</button><button role="tab" aria-selected="{current === 'second'}" @click="current = 'second'" class="min-h-10 flex-1 rounded-lg px-4 text-sm font-semibold transition {current === 'second' ? 'bg-white shadow dark:bg-slate-900' : 'text-slate-500'}">Second</button></div><div data-show="current === 'first'" role="tabpanel" class="pt-5"><slot name="first" /></div><div data-show="current === 'second'" role="tabpanel" class="pt-5"><slot name="second" /></div></div>
<div class="{class}"><div role="tablist" class="flex gap-1 overflow-x-auto rounded-xl bg-slate-100 p-1 dark:bg-slate-800"><button role="tab" aria-selected="{current === 'first'}" @click="current = 'first'" class="min-h-10 flex-1 rounded-lg px-4 text-sm font-semibold transition {current === 'first' ? 'bg-white shadow dark:bg-slate-900' : 'text-slate-500'}">First</button><button role="tab" aria-selected="{current === 'second'}" @click="current = 'second'" class="min-h-10 flex-1 rounded-lg px-4 text-sm font-semibold transition {current === 'second' ? 'bg-white shadow dark:bg-slate-900' : 'text-slate-500'}">Second</button></div><div data-show="current === 'first'" role="tabpanel" class="pt-5"><slot name="first" /></div><div data-show="current === 'second'" role="tabpanel" class="pt-5"><slot name="second" /></div></div>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component TestimonialCard {
props {
class = ""
quote = "Great product."
name = "Customer"
role = ""
@@ -7,6 +8,6 @@ component TestimonialCard {
avatar = ""
}
view {
<figure class="rounded-3xl border border-slate-200 bg-white p-7 shadow-sm dark:border-slate-800 dark:bg-slate-900"><blockquote class="text-lg leading-8 text-slate-700 dark:text-slate-200">“{quote}”</blockquote><figcaption class="mt-6 flex items-center gap-3"><Avatar src="{avatar}" initials="{name.slice(0,2)}" /><div><div class="font-semibold">{name}</div><div class="text-sm text-slate-500">{role}<span data-show="company"> · {company}</span></div></div></figcaption></figure>
<figure class="rounded-3xl border border-slate-200 bg-white p-7 shadow-sm dark:border-slate-800 dark:bg-slate-900 {class}"><blockquote class="text-lg leading-8 text-slate-700 dark:text-slate-200">“{quote}”</blockquote><figcaption class="mt-6 flex items-center gap-3"><Avatar src="{avatar}" initials="{name.slice(0,2)}" /><div><div class="font-semibold">{name}</div><div class="text-sm text-slate-500">{role}<span data-show="company"> · {company}</span></div></div></figcaption></figure>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component TextInput {
props {
class = ""
id = ""
name = ""
label = "Text"
@@ -13,7 +14,7 @@ component TextInput {
autocomplete = ""
}
view {
<div class="space-y-2">
<div class="space-y-2 {class}">
<label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label>
<input id="{id}" name="{name}" type="text" value="{value}" placeholder="{placeholder}" required="{required}" disabled="{disabled}" readonly="{readonly}" autocomplete="{autocomplete}" aria-invalid="{!!error}" aria-describedby="{id}-help {id}-error" class="min-h-11 w-full rounded-xl border bg-white px-3.5 py-2.5 text-slate-950 shadow-sm outline-none transition duration-200 placeholder:text-slate-400 focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 disabled:cursor-not-allowed disabled:bg-slate-100 disabled:opacity-70 dark:bg-slate-950 dark:text-white {error ? 'border-red-500 focus:border-red-500 focus:ring-red-500/10' : 'border-slate-300 dark:border-slate-700'}" />
<p id="{id}-help" data-show="help && !error" class="text-sm text-slate-500 dark:text-slate-400">{help}</p>
+2 -1
View File
@@ -1,5 +1,6 @@
component TimeInput {
props {
class = ""
id = ""
name = ""
label = "Time"
@@ -13,7 +14,7 @@ component TimeInput {
autocomplete = ""
}
view {
<div class="space-y-2">
<div class="space-y-2 {class}">
<label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label>
<input id="{id}" name="{name}" type="time" value="{value}" placeholder="{placeholder}" required="{required}" disabled="{disabled}" readonly="{readonly}" autocomplete="{autocomplete}" aria-invalid="{!!error}" aria-describedby="{id}-help {id}-error" class="min-h-11 w-full rounded-xl border bg-white px-3.5 py-2.5 text-slate-950 shadow-sm outline-none transition duration-200 placeholder:text-slate-400 focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 disabled:cursor-not-allowed disabled:bg-slate-100 disabled:opacity-70 dark:bg-slate-950 dark:text-white {error ? 'border-red-500 focus:border-red-500 focus:ring-red-500/10' : 'border-slate-300 dark:border-slate-700'}" />
<p id="{id}-help" data-show="help && !error" class="text-sm text-slate-500 dark:text-slate-400">{help}</p>
+2 -1
View File
@@ -1,8 +1,9 @@
component Timeline {
props {
class = ""
title = "Timeline"
}
view {
<section><h2 class="text-lg font-bold">{title}</h2><ol class="mt-5 border-l border-slate-200 pl-6 dark:border-slate-800"><slot /></ol></section>
<section class="{class}"><h2 class="text-lg font-bold">{title}</h2><ol class="mt-5 border-l border-slate-200 pl-6 dark:border-slate-800"><slot /></ol></section>
}
}
+2 -1
View File
@@ -1,11 +1,12 @@
component TimelineItem {
props {
class = ""
title = "Event"
date = ""
description = ""
status = "default"
}
view {
<li class="relative pb-8 last:pb-0"><span class="absolute -left-[1.85rem] top-1 size-3 rounded-full ring-4 ring-white dark:ring-slate-950 {status === 'success' ? 'bg-emerald-500' : status === 'warning' ? 'bg-amber-500' : 'bg-violet-500'}"></span><div class="flex flex-wrap items-baseline justify-between gap-2"><h3 class="font-semibold">{title}</h3><time class="text-xs text-slate-500">{date}</time></div><p data-show="description" class="mt-1 text-sm text-slate-500">{description}</p></li>
<li class="relative {class}"><span class="absolute -left-[1.85rem] top-1 size-3 rounded-full ring-4 ring-white dark:ring-slate-950 {status === 'success' ? 'bg-emerald-500' : status === 'warning' ? 'bg-amber-500' : 'bg-violet-500'}"></span><div class="flex flex-wrap items-baseline justify-between gap-2"><h3 class="font-semibold">{title}</h3><time class="text-xs text-slate-500">{date}</time></div><p data-show="description" class="mt-1 text-sm text-slate-500">{description}</p></li>
}
}
+2 -1
View File
@@ -1,10 +1,11 @@
component TimezoneSelector {
props {
class = ""
id = "timezone"
name = "timezone"
label = "Timezone"
}
view {
<Select id="{id}" name="{name}" label="{label}"><option value="UTC">UTC</option><option value="Asia/Kolkata">Asia/Kolkata</option><option value="America/New_York">America/New_York</option><option value="Europe/London">Europe/London</option></Select>
<Select class="{class}" id="{id}" name="{name}" label="{label}"><option value="UTC">UTC</option><option value="Asia/Kolkata">Asia/Kolkata</option><option value="America/New_York">America/New_York</option><option value="Europe/London">Europe/London</option></Select>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component Toast {
props {
class = ""
title = "Saved"
description = ""
variant = "success"
@@ -7,6 +8,6 @@ component Toast {
}
state visible = true
view {
<div data-show="visible" role="status" aria-live="polite" class="pointer-events-auto flex w-full max-w-sm items-start gap-3 rounded-2xl border bg-white p-4 shadow-2xl dark:border-slate-800 dark:bg-slate-900"><span class="mt-0.5 flex size-8 items-center justify-center rounded-full {variant === 'success' ? 'bg-emerald-100 text-emerald-700' : variant === 'danger' ? 'bg-red-100 text-red-700' : 'bg-blue-100 text-blue-700'}">{variant === 'success' ? '✓' : variant === 'danger' ? '!' : 'i'}</span><div class="min-w-0 flex-1"><p class="font-semibold">{title}</p><p data-show="description" class="mt-1 text-sm text-slate-500">{description}</p></div><button type="button" aria-label="Close" @click="visible = false" class="rounded-lg p-1 text-slate-400 hover:bg-slate-100">×</button></div>
<div data-show="visible" role="status" aria-live="polite" class="pointer-events-auto flex w-full max-w-sm items-start gap-3 rounded-2xl border bg-white p-4 shadow-2xl dark:border-slate-800 dark:bg-slate-900 {class}"><span class="mt-0.5 flex size-8 items-center justify-center rounded-full {variant === 'success' ? 'bg-emerald-100 text-emerald-700' : variant === 'danger' ? 'bg-red-100 text-red-700' : 'bg-blue-100 text-blue-700'}">{variant === 'success' ? '✓' : variant === 'danger' ? '!' : 'i'}</span><div class="min-w-0 flex-1"><p class="font-semibold">{title}</p><p data-show="description" class="mt-1 text-sm text-slate-500">{description}</p></div><button type="button" aria-label="Close" @click="visible = false" class="rounded-lg p-1 text-slate-400 hover:bg-slate-100">×</button></div>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component UrlInput {
props {
class = ""
id = ""
name = ""
label = "Url"
@@ -13,7 +14,7 @@ component UrlInput {
autocomplete = ""
}
view {
<div class="space-y-2">
<div class="space-y-2 {class}">
<label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label>
<input id="{id}" name="{name}" type="url" value="{value}" placeholder="{placeholder}" required="{required}" disabled="{disabled}" readonly="{readonly}" autocomplete="{autocomplete}" aria-invalid="{!!error}" aria-describedby="{id}-help {id}-error" class="min-h-11 w-full rounded-xl border bg-white px-3.5 py-2.5 text-slate-950 shadow-sm outline-none transition duration-200 placeholder:text-slate-400 focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 disabled:cursor-not-allowed disabled:bg-slate-100 disabled:opacity-70 dark:bg-slate-950 dark:text-white {error ? 'border-red-500 focus:border-red-500 focus:ring-red-500/10' : 'border-slate-300 dark:border-slate-700'}" />
<p id="{id}-help" data-show="help && !error" class="text-sm text-slate-500 dark:text-slate-400">{help}</p>
+2 -1
View File
@@ -1,5 +1,6 @@
component Alert {
props {
class = ""
title = "Notice"
description = ""
variant = "info"
@@ -12,7 +13,7 @@ component Alert {
<div
data-show="visible"
role="alert"
class="flex items-start gap-3 rounded-2xl border p-4 {variant === 'success' ? 'border-emerald-200 bg-emerald-50 text-emerald-900 dark:border-emerald-900 dark:bg-emerald-950/40 dark:text-emerald-200' : variant === 'warning' ? 'border-amber-200 bg-amber-50 text-amber-900 dark:border-amber-900 dark:bg-amber-950/40 dark:text-amber-200' : variant === 'danger' ? 'border-red-200 bg-red-50 text-red-900 dark:border-red-900 dark:bg-red-950/40 dark:text-red-200' : 'border-blue-200 bg-blue-50 text-blue-900 dark:border-blue-900 dark:bg-blue-950/40 dark:text-blue-200'}"
class="flex items-start gap-3 rounded-2xl border p-4 {variant === 'success' ? 'border-emerald-200 bg-emerald-50 text-emerald-900 dark:border-emerald-900 dark:bg-emerald-950/40 dark:text-emerald-200' : variant === 'warning' ? 'border-amber-200 bg-amber-50 text-amber-900 dark:border-amber-900 dark:bg-amber-950/40 dark:text-amber-200' : variant === 'danger' ? 'border-red-200 bg-red-50 text-red-900 dark:border-red-900 dark:bg-red-950/40 dark:text-red-200' : 'border-blue-200 bg-blue-50 text-blue-900 dark:border-blue-900 dark:bg-blue-950/40 dark:text-blue-200'} {class}"
>
<div class="min-w-0 flex-1" >
<h3 class="font-semibold" >
+2 -1
View File
@@ -1,11 +1,12 @@
component Avatar {
props {
class = ""
src = ""
alt = ""
initials = "WR"
size = "md"
}
view {
<span class="inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-gradient-to-br from-violet-500 to-fuchsia-500 font-semibold text-white {size === 'sm' ? 'size-8 text-xs' : size === 'lg' ? 'size-14 text-base' : 'size-10 text-sm'}"><img data-show="src" src="{src}" alt="{alt}" class="h-full w-full object-cover" /><span data-show="!src" aria-hidden="true">{initials}</span></span>
<span class="inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-gradient-to-br from-violet-500 to-fuchsia-500 font-semibold text-white {size === 'sm' ? 'size-8 text-xs' : size === 'lg' ? 'size-14 text-base' : 'size-10 text-sm'} {class}"><img data-show="src" src="{src}" alt="{alt}" class="h-full w-full object-cover" /><span data-show="!src" aria-hidden="true">{initials}</span></span>
}
}
+2 -1
View File
@@ -1,9 +1,10 @@
component Badge {
props {
class = ""
label = "Badge"
variant = "neutral"
}
view {
<span class="inline-flex items-center rounded-full px-2.5 py-1 text-xs font-semibold ring-1 ring-inset {variant === 'success' ? 'bg-emerald-50 text-emerald-700 ring-emerald-600/20 dark:bg-emerald-950/40 dark:text-emerald-300' : variant === 'warning' ? 'bg-amber-50 text-amber-700 ring-amber-600/20 dark:bg-amber-950/40 dark:text-amber-300' : variant === 'danger' ? 'bg-red-50 text-red-700 ring-red-600/20 dark:bg-red-950/40 dark:text-red-300' : variant === 'brand' ? 'bg-violet-50 text-violet-700 ring-violet-600/20 dark:bg-violet-950/40 dark:text-violet-300' : 'bg-slate-100 text-slate-700 ring-slate-600/10 dark:bg-slate-800 dark:text-slate-300'}">{label}</span>
<span class="inline-flex items-center rounded-full px-2.5 py-1 text-xs font-semibold ring-1 ring-inset {variant === 'success' ? 'bg-emerald-50 text-emerald-700 ring-emerald-600/20 dark:bg-emerald-950/40 dark:text-emerald-300' : variant === 'warning' ? 'bg-amber-50 text-amber-700 ring-amber-600/20 dark:bg-amber-950/40 dark:text-amber-300' : variant === 'danger' ? 'bg-red-50 text-red-700 ring-red-600/20 dark:bg-red-950/40 dark:text-red-300' : variant === 'brand' ? 'bg-violet-50 text-violet-700 ring-violet-600/20 dark:bg-violet-950/40 dark:text-violet-300' : 'bg-slate-100 text-slate-700 ring-slate-600/10 dark:bg-slate-800 dark:text-slate-300'} {class}">{label}</span>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component Checkbox {
props {
class = ""
id = ""
name = ""
label = "Checkbox"
@@ -8,6 +9,6 @@ component Checkbox {
disabled = false
}
view {
<label for="{id}" class="flex min-h-11 cursor-pointer items-start gap-3 rounded-xl p-2 transition hover:bg-slate-50 dark:hover:bg-slate-900"><input id="{id}" name="{name}" type="checkbox" checked="{checked}" disabled="{disabled}" class="mt-0.5 size-5 rounded border-slate-300 text-violet-600 focus:ring-violet-500 dark:border-slate-700 dark:bg-slate-950" /><span><span class="block text-sm font-semibold text-slate-900 dark:text-white">{label}</span><span data-show="description" class="mt-0.5 block text-sm text-slate-500 dark:text-slate-400">{description}</span></span></label>
<label for="{id}" class="flex min-h-11 cursor-pointer items-start gap-3 rounded-xl p-2 transition hover:bg-slate-50 dark:hover:bg-slate-900 {class}"><input id="{id}" name="{name}" type="checkbox" checked="{checked}" disabled="{disabled}" class="mt-0.5 size-5 rounded border-slate-300 text-violet-600 focus:ring-violet-500 dark:border-slate-700 dark:bg-slate-950" /><span><span class="block text-sm font-semibold text-slate-900 dark:text-white">{label}</span><span data-show="description" class="mt-0.5 block text-sm text-slate-500 dark:text-slate-400">{description}</span></span></label>
}
}
+1 -1
View File
@@ -4,6 +4,6 @@ component Container {
className = ""
}
view {
<div class="mx-auto w-full px-4 sm:px-6 lg:px-8 {size === 'narrow' ? 'max-w-3xl' : size === 'wide' ? 'max-w-screen-2xl' : 'max-w-7xl'} {className}"><slot /></div>
<div class="w-full {size === 'narrow' ? 'max-w-3xl' : size === 'wide' ? 'max-w-screen-2xl' : 'max-w-7xl'} {className}"><slot /></div>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component Radio {
props {
class = ""
id = ""
name = "choice"
value = ""
@@ -9,6 +10,6 @@ component Radio {
disabled = false
}
view {
<label for="{id}" class="flex min-h-11 cursor-pointer items-start gap-3 rounded-xl border border-slate-200 p-3 transition hover:border-violet-400 dark:border-slate-800"><input id="{id}" name="{name}" type="radio" value="{value}" checked="{checked}" disabled="{disabled}" class="mt-0.5 size-5 border-slate-300 text-violet-600 focus:ring-violet-500" /><span><span class="block text-sm font-semibold">{label}</span><span data-show="description" class="text-sm text-slate-500">{description}</span></span></label>
<label for="{id}" class="flex min-h-11 cursor-pointer items-start gap-3 rounded-xl border border-slate-200 p-3 transition hover:border-violet-400 dark:border-slate-800 {class}"><input id="{id}" name="{name}" type="radio" value="{value}" checked="{checked}" disabled="{disabled}" class="mt-0.5 size-5 border-slate-300 text-violet-600 focus:ring-violet-500" /><span><span class="block text-sm font-semibold">{label}</span><span data-show="description" class="text-sm text-slate-500">{description}</span></span></label>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component Select {
props {
class = ""
id = ""
name = ""
label = "Select"
@@ -9,6 +10,6 @@ component Select {
disabled = false
}
view {
<div class="space-y-2"><label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label><select id="{id}" name="{name}" required="{required}" disabled="{disabled}" class="min-h-11 w-full rounded-xl border border-slate-300 bg-white px-3.5 py-2.5 text-slate-950 shadow-sm outline-none transition focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 dark:border-slate-700 dark:bg-slate-950 dark:text-white"><slot /></select><p data-show="help && !error" class="text-sm text-slate-500">{help}</p><p data-show="error" role="alert" class="text-sm font-medium text-red-600">{error}</p></div>
<div class="space-y-2 {class}"><label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label><select id="{id}" name="{name}" required="{required}" disabled="{disabled}" class="min-h-11 w-full rounded-xl border border-slate-300 bg-white px-3.5 py-2.5 text-slate-950 shadow-sm outline-none transition focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 dark:border-slate-700 dark:bg-slate-950 dark:text-white"><slot /></select><p data-show="help && !error" class="text-sm text-slate-500">{help}</p><p data-show="error" role="alert" class="text-sm font-medium text-red-600">{error}</p></div>
}
}
+2 -1
View File
@@ -1,9 +1,10 @@
component Spinner {
props {
class = ""
label = "Loading"
size = "md"
}
view {
<span role="status" aria-label="{label}" class="inline-block animate-spin rounded-full border-2 border-slate-300 border-r-violet-600 {size === 'sm' ? 'size-4' : size === 'lg' ? 'size-10' : 'size-6'}"></span>
<span role="status" aria-label="{label}" class="inline-block animate-spin rounded-full border-2 border-slate-300 border-r-violet-600 {size === 'sm' ? 'size-4' : size === 'lg' ? 'size-10' : 'size-6'} {class}"></span>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component Switch {
props {
class = ""
id = ""
name = ""
label = "Switch"
@@ -9,6 +10,6 @@ component Switch {
}
state on = checked
view {
<button id="{id}" type="button" role="switch" aria-checked="{on}" disabled="{disabled}" @click="on = !on" class="flex w-full items-center justify-between gap-4 rounded-xl p-2 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-500 disabled:opacity-50"><span><span class="block text-sm font-semibold">{label}</span><span data-show="description" class="text-sm text-slate-500">{description}</span></span><span class="relative inline-flex h-6 w-11 shrink-0 rounded-full transition {on ? 'bg-violet-600' : 'bg-slate-300 dark:bg-slate-700'}"><span class="absolute top-0.5 size-5 rounded-full bg-white shadow transition-transform {on ? 'translate-x-5' : 'translate-x-0.5'}"></span></span><input type="hidden" name="{name}" value="{on ? 'true' : 'false'}" /></button>
<button id="{id}" type="button" role="switch" aria-checked="{on}" disabled="{disabled}" @click="on = !on" class="flex w-full items-center justify-between gap-4 rounded-xl p-2 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-500 disabled:opacity-50 {class}"><span><span class="block text-sm font-semibold">{label}</span><span data-show="description" class="text-sm text-slate-500">{description}</span></span><span class="relative inline-flex h-6 w-11 shrink-0 rounded-full transition {on ? 'bg-violet-600' : 'bg-slate-300 dark:bg-slate-700'}"><span class="absolute top-0.5 size-5 rounded-full bg-white shadow transition-transform {on ? 'translate-x-5' : 'translate-x-0.5'}"></span></span><input type="hidden" name="{name}" value="{on ? 'true' : 'false'}" /></button>
}
}
+2 -1
View File
@@ -1,9 +1,10 @@
component Table {
props {
class = ""
caption = "Data table"
responsive = true
}
view {
<div class="w-full overflow-x-auto rounded-2xl border border-slate-200 dark:border-slate-800"><table class="w-full min-w-[640px] border-collapse text-left text-sm"><caption class="sr-only">{caption}</caption><slot /></table></div>
<div class="w-full overflow-x-auto rounded-2xl border border-slate-200 dark:border-slate-800 {class}"><table class="w-full min-w-[640px] border-collapse text-left text-sm"><caption class="sr-only">{caption}</caption><slot /></table></div>
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
component Textarea {
props {
class = ""
id = ""
name = ""
label = "Message"
@@ -12,6 +13,6 @@ component Textarea {
disabled = false
}
view {
<div class="space-y-2"><label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label><textarea id="{id}" name="{name}" rows="{rows}" placeholder="{placeholder}" required="{required}" disabled="{disabled}" aria-invalid="{!!error}" class="w-full resize-y rounded-xl border bg-white px-3.5 py-3 text-slate-950 shadow-sm outline-none transition focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 dark:bg-slate-950 dark:text-white {error ? 'border-red-500' : 'border-slate-300 dark:border-slate-700'}">{value}</textarea><p data-show="help && !error" class="text-sm text-slate-500">{help}</p><p data-show="error" role="alert" class="text-sm font-medium text-red-600">{error}</p></div>
<div class="space-y-2 {class}"><label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label><textarea id="{id}" name="{name}" rows="{rows}" placeholder="{placeholder}" required="{required}" disabled="{disabled}" aria-invalid="{!!error}" class="w-full resize-y rounded-xl border bg-white px-3.5 py-3 text-slate-950 shadow-sm outline-none transition focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 dark:bg-slate-950 dark:text-white {error ? 'border-red-500' : 'border-slate-300 dark:border-slate-700'}">{value}</textarea><p data-show="help && !error" class="text-sm text-slate-500">{help}</p><p data-show="error" role="alert" class="text-sm font-medium text-red-600">{error}</p></div>
}
}
+2 -1
View File
@@ -1,9 +1,10 @@
component Tooltip {
props {
class = ""
text = "Helpful information"
position = "top"
}
view {
<span class="group relative inline-flex"><slot /><span role="tooltip" class="pointer-events-none absolute z-40 w-max max-w-xs rounded-lg bg-slate-950 px-2.5 py-1.5 text-xs font-medium text-white opacity-0 shadow-lg transition duration-200 group-hover:opacity-100 group-focus-within:opacity-100 {position === 'bottom' ? 'left-1/2 top-full mt-2 -translate-x-1/2' : 'bottom-full left-1/2 mb-2 -translate-x-1/2'}">{text}</span></span>
<span class="group relative inline-flex {class}"><slot /><span role="tooltip" class="pointer-events-none absolute z-40 w-max max-w-xs rounded-lg bg-slate-950 px-2.5 py-1.5 text-xs font-medium text-white opacity-0 shadow-lg transition duration-200 group-hover:opacity-100 group-focus-within:opacity-100 {position === 'bottom' ? 'left-1/2 top-full mt-2 -translate-x-1/2' : 'bottom-full left-1/2 mb-2 -translate-x-1/2'}">{text}</span></span>
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ui",
"version": "0.2.60",
"version": "0.2.61",
"private": true,
"type": "module",
"main": "src/index.ts",
+28
View File
@@ -87,6 +87,34 @@ test("page shells leave background ownership to the page", () => {
expect(shellRule).not.toMatch(/background(?:-color)?\s*:/);
});
test("component boundaries have no default margins and expose class customization", () => {
for (const name of uiComponentNames()) {
const source = readFileSync(join(uiComponentsDir(), `${name}.wrn`), "utf8");
const root = source.match(/view\s*\{\s*<[A-Za-z][^>]*>/)?.[0] ?? "";
const classes = root.match(/\bclass="([^"]*)"/)?.[1]?.split(/\s+/) ?? [];
expect(classes.filter((token) => /^-?m[trblxy]?-/.test(token))).toEqual([]);
expect(source).toMatch(/^\s*(?:class|className)\s*(?::[^=\r\n]+)?=/m);
expect(root).toMatch(/\{class(?:Name)?\}/);
}
});
test("layout boundaries do not add default padding", () => {
for (const name of [
"Container",
"CTASection",
"EmptyState",
"ErrorState",
"Hero",
"LegalDocumentLayout",
"TimelineItem",
]) {
const source = readFileSync(join(uiComponentsDir(), `${name}.wrn`), "utf8");
const root = source.match(/view\s*\{\s*<[A-Za-z][^>]*>/)?.[0] ?? "";
const classes = root.match(/\bclass="([^"]*)"/)?.[1]?.split(/\s+/) ?? [];
expect(classes.filter((token) => /^p[trblxy]?-/.test(token))).toEqual([]);
}
});
test("every bundled UI component compiles", () => {
for (const name of uiComponentNames()) {
const path = join(uiComponentsDir(), `${name}.wrn`);
+2 -4
View File
@@ -29,7 +29,7 @@
.wire-divider {
border: 0;
border-top: 1px solid var(--wire-color-border);
margin: 1rem 0;
margin: 0;
}
/* gap scale (shared by stack / hstack / grid) */
@@ -790,8 +790,6 @@
grid-template-columns: minmax(14rem, 18rem) minmax(0, 1fr);
gap: clamp(2rem, 5vw, 5rem);
max-width: 80rem;
margin-inline: auto;
padding: clamp(1rem, 4vw, 3rem);
}
.wire-page-shell__aside {
position: sticky;
@@ -895,7 +893,7 @@
.wire-cookie-list {
display: grid;
gap: 0.75rem;
margin-top: 1.25rem;
margin: 0;
}
.wire-cookie-option {
display: flex;