@ziyarago/ui 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [0.6.0](https://github.com/ziyarago/ui/compare/v0.5.0...v0.6.0) (2026-10-21)
2
+
3
+ ### Features
4
+
5
+ * SocialAuth component with Google and Apple buttons ([398b36d](https://github.com/ziyarago/ui/commit/398b36dcc808e8f4656c09e8c12acf2e21c6084d))
6
+
7
+ ## [0.5.0](https://github.com/ziyarago/ui/compare/v0.4.0...v0.5.0) (2026-10-21)
8
+
9
+ ### Features
10
+
11
+ * AuthShowcase login layouts (photo, light, tint) and PageAuth variants ([4ea5503](https://github.com/ziyarago/ui/commit/4ea5503e223f2c651292372be91987538f0517c8))
12
+
1
13
  ## [0.4.0](https://github.com/ziyarago/ui/compare/v0.3.0...v0.4.0) (2026-10-21)
2
14
 
3
15
  ### Features
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  <p align="center"><a href="https://ziyara.vip"><img src="https://raw.githubusercontent.com/ziyarago/ui/main/docs/images/hero-dark.png" alt="ziyarago/ui: glass design system for Vue, Nuxt and React" width="100%"></a></p>
6
6
 
7
- Glass design system for Vue 3, Nuxt and React: 182 components, 17 page templates, OKLCH tokens, dark and light, RTL and Arabic, 16 interface languages. One framework-neutral core with thin adapters. Built for Umrah and Hajj products, usable anywhere.
7
+ Glass design system for Vue 3, Nuxt and React: 184 components, 17 page templates, OKLCH tokens, dark and light, RTL and Arabic, 16 interface languages. One framework-neutral core with thin adapters. Built for Umrah and Hajj products, usable anywhere.
8
8
 
9
9
  <table>
10
10
  <tr>
@@ -12,7 +12,7 @@ Glass design system for Vue 3, Nuxt and React: 182 components, 17 page templates
12
12
  <td width="50%"><img src="https://raw.githubusercontent.com/ziyarago/ui/main/docs/images/hero-rtl.png" alt="Arabic RTL"><br><sub>Arabic, RTL mirrored</sub></td>
13
13
  </tr>
14
14
  <tr>
15
- <td><img src="https://raw.githubusercontent.com/ziyarago/ui/main/docs/images/components.png" alt="Component catalog"><br><sub>182 components with props and live examples</sub></td>
15
+ <td><img src="https://raw.githubusercontent.com/ziyarago/ui/main/docs/images/components.png" alt="Component catalog"><br><sub>184 components with props and live examples</sub></td>
16
16
  <td><img src="https://raw.githubusercontent.com/ziyarago/ui/main/docs/images/templates.png" alt="Page templates"><br><sub>17 page templates</sub></td>
17
17
  </tr>
18
18
  <tr>
@@ -24,7 +24,7 @@ Glass design system for Vue 3, Nuxt and React: 182 components, 17 page templates
24
24
  Files:
25
25
 
26
26
  ```
27
- src/core/ components*.js, pages.js: 182 components and 17 page templates as { state, slots, render(ctx) }; no framework imports
27
+ src/core/ components*.js, pages.js: 184 components and 17 page templates as { state, slots, render(ctx) }; no framework imports
28
28
  src/vue/ Vue 3 adapter (UiButton, UiAppShell … + install(app))
29
29
  src/react/ React adapter (same names)
30
30
  src/theme.js useUiTheme(): theme · palette · dir → <html> attributes, persisted
@@ -1,8 +1,8 @@
1
1
  # @ziyarago/ui
2
2
 
3
- > ZiyaraGo UI — glass design system for Umrah and Hajj products. OKLCH tokens, two palettes, dark and light, RTL, Thmanyah. One framework-neutral core with Vue 3, Nuxt and React adapters, 182 components and 17 page templates.
3
+ > ZiyaraGo UI — glass design system for Umrah and Hajj products. OKLCH tokens, two palettes, dark and light, RTL, Thmanyah. One framework-neutral core with Vue 3, Nuxt and React adapters, 184 components and 17 page templates.
4
4
 
5
- Version 0.4.0. Docs: https://ziyara.vip . Manifest (machine readable): https://ziyara.vip/manifest.json
5
+ Version 0.6.0. Docs: https://ziyara.vip . Manifest (machine readable): https://ziyara.vip/manifest.json
6
6
 
7
7
  ## Setup
8
8
  - import { UiButton } from '@ziyarago/ui/vue'
@@ -2110,6 +2110,41 @@ Slots:
2110
2110
  Notes:
2111
2111
  - logo and aside are props in both Vue and React (pass a VNode / element).
2112
2112
 
2113
+ ## UiAuthShowcase (domain)
2114
+ Sign-in layout with a media panel: dark photo card with floating avatars, light gradient card with a social-proof pill, or a tinted panel with a glass caption. The default slot is the form.
2115
+ Props:
2116
+ - `variant`: 'photo' | 'light' | 'tint' = "photo". 'photo': one framed card, media on the left. 'light': gradient form card, proof pill below, glass caption on the media. 'tint': like light, with a coloured panel, a heading on the media and cyan accent.
2117
+ - `title` (required): string. Form heading.
2118
+ - `subtitle`: string. Text under the heading.
2119
+ - `logo`: VNode. Logo node above the heading.
2120
+ - `footer`: VNode. Content under the fields, for example "No account? Sign up".
2121
+ - `image`: string. URL of the media image. Without it the panel shows an accent gradient.
2122
+ - `headline`: string. Large text on the media panel (photo and tint).
2123
+ - `caption`: string. Small text on the media panel (photo) or inside the glass bar (light, tint).
2124
+ - `quote`: string. Line at the bottom of the media panel; photo only.
2125
+ - `chips`: string[]. Pill labels inside the glass bar; light and tint.
2126
+ - `people`: { name: string; src?: string }[]. Avatars: floating on the photo, stacked in the proof pill.
2127
+ - `proof`: string. Social-proof title, e.g. "Join 20k+ users"; light and tint. Shows the pill under the form.
2128
+ - `proofHint`: string. Second line of the proof pill.
2129
+ Slots:
2130
+ - default: Form fields and buttons. React passes them as children.
2131
+ Notes:
2132
+ - logo, footer, image, people are props in both Vue and React. The media panel is hidden below the form on narrow screens (stacks under it).
2133
+
2134
+ ## UiSocialAuth (domain)
2135
+ Continue with Google and Apple buttons with an "or" divider. Place it under the sign-in form.
2136
+ Props:
2137
+ - `providers`: ('google' | 'apple')[] = "['google', 'apple']". Which buttons to show, in order.
2138
+ - `variant`: 'stack' | 'icons' = "stack". 'stack': full-width buttons with a label, one per row. 'icons': a row of square icon buttons.
2139
+ - `divider`: boolean = "true". Show the "or" line above the buttons.
2140
+ - `labels`: { google?: string; apple?: string }. Provider names inserted into "Continue with {provider}".
2141
+ - `onSelect`: (provider: 'google' | 'apple') => void. Called with the clicked provider. Start your OAuth flow here.
2142
+ - `size`: 'xs' | 'sm' | 'md' | 'lg' | 'xl' = "md". Scales padding, text and avatars.
2143
+ Events:
2144
+ - select: A provider button was clicked; payload is "google" or "apple".
2145
+ Notes:
2146
+ - The kit only renders the buttons. The OAuth flow itself is yours.
2147
+
2113
2148
  ## UiPageDashboard (pages)
2114
2149
  Dashboard template: greeting hero, KPI tiles, occupancy chart, sales funnel, top list and activity feed.
2115
2150
  Props:
@@ -2351,7 +2386,15 @@ Notes:
2351
2386
  ## UiPageAuth (pages)
2352
2387
  Sign-in page template without the app shell, as a centered card or a split layout.
2353
2388
  Props:
2354
- - `layout`: 'card' | 'split' = "card". 'split' uses AuthSplit with a side panel; anything else renders a centered AuthLayout card over the background.
2389
+ - `layout`: 'card' | 'split' | 'photo' | 'light' | 'tint' = "card". 'split' uses AuthSplit with a side panel. 'photo', 'light' and 'tint' use AuthShowcase with a media panel. Anything else renders a centered AuthLayout card over the background.
2390
+ - `image`: string. Media image URL; photo, light and tint layouts.
2391
+ - `headline`: string. Large text on the media panel; photo and tint.
2392
+ - `caption`: string. Small text on the media panel or glass bar.
2393
+ - `quote`: string. Bottom line of the media panel; photo only.
2394
+ - `chips`: string[]. Pill labels in the glass bar; light and tint.
2395
+ - `people`: { name: string; src?: string }[]. Avatars on the media panel or in the proof pill.
2396
+ - `proof`: string. Social-proof title under the form; light and tint.
2397
+ - `proofHint`: string. Second line of the proof pill.
2355
2398
  - `title`: string. Form heading.
2356
2399
  - `subtitle`: string. Text under the heading.
2357
2400
  Slots:
package/dist/llms.txt CHANGED
@@ -1,8 +1,8 @@
1
1
  # @ziyarago/ui
2
2
 
3
- > ZiyaraGo UI — glass design system for Umrah and Hajj products. OKLCH tokens, two palettes, dark and light, RTL, Thmanyah. One framework-neutral core with Vue 3, Nuxt and React adapters, 182 components and 17 page templates.
3
+ > ZiyaraGo UI — glass design system for Umrah and Hajj products. OKLCH tokens, two palettes, dark and light, RTL, Thmanyah. One framework-neutral core with Vue 3, Nuxt and React adapters, 184 components and 17 page templates.
4
4
 
5
- Version 0.4.0. Docs: https://ziyara.vip . Manifest (machine readable): https://ziyara.vip/manifest.json
5
+ Version 0.6.0. Docs: https://ziyara.vip . Manifest (machine readable): https://ziyara.vip/manifest.json
6
6
 
7
7
  ## Setup
8
8
  - import { UiButton } from '@ziyarago/ui/vue'
@@ -214,6 +214,8 @@ Umrah, Hajj, hotel and CRM blocks.
214
214
  - UiCallCard: Call recording card with status badge, a playback progress bar and duration.
215
215
  - UiIncomingCall: Glass banner for an incoming call with Decline and Answer buttons.
216
216
  - UiAuthSplit: Two-column sign-in layout with a brand panel on the left and the form on the right.
217
+ - UiAuthShowcase: Sign-in layout with a media panel: dark photo card with floating avatars, light gradient card with a social-proof pill, or a tinted panel with a glass caption. The default slot is the form.
218
+ - UiSocialAuth: Continue with Google and Apple buttons with an "or" divider. Place it under the sign-in form.
217
219
 
218
220
  ## Page templates
219
221
  Ready layouts with named slots.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziyarago/ui",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "homepage": "https://ziyara.vip",
5
5
  "imports": {
6
6
  "vue": "import { UiButton } from '@ziyarago/ui/vue'",
@@ -251,7 +251,9 @@
251
251
  "CallRow",
252
252
  "CallCard",
253
253
  "IncomingCall",
254
- "AuthSplit"
254
+ "AuthSplit",
255
+ "AuthShowcase",
256
+ "SocialAuth"
255
257
  ]
256
258
  },
257
259
  {
@@ -2273,7 +2275,7 @@
2273
2275
  "id": "auth",
2274
2276
  "label": "Authentication (6)",
2275
2277
  "component": "UiPageAuth",
2276
- "description": "Six sign-in screens in two layouts: centered card and split with a side panel. Always dark theme.",
2278
+ "description": "Seven sign-in screens in five layouts: centered card, split with a side panel, and three with a media panel (photo, light, tint).",
2277
2279
  "shell": false,
2278
2280
  "slots": [
2279
2281
  {
@@ -10050,6 +10052,132 @@
10050
10052
  "logo and aside are props in both Vue and React (pass a VNode / element)."
10051
10053
  ]
10052
10054
  },
10055
+ {
10056
+ "name": "AuthShowcase",
10057
+ "tag": "UiAuthShowcase",
10058
+ "group": "domain",
10059
+ "description": "Sign-in layout with a media panel: dark photo card with floating avatars, light gradient card with a social-proof pill, or a tinted panel with a glass caption. The default slot is the form.",
10060
+ "props": {
10061
+ "variant": {
10062
+ "type": "'photo' | 'light' | 'tint'",
10063
+ "values": [
10064
+ "photo",
10065
+ "light",
10066
+ "tint"
10067
+ ],
10068
+ "default": "photo",
10069
+ "description": "'photo': one framed card, media on the left. 'light': gradient form card, proof pill below, glass caption on the media. 'tint': like light, with a coloured panel, a heading on the media and cyan accent."
10070
+ },
10071
+ "title": {
10072
+ "type": "string",
10073
+ "required": true,
10074
+ "description": "Form heading."
10075
+ },
10076
+ "subtitle": {
10077
+ "type": "string",
10078
+ "description": "Text under the heading."
10079
+ },
10080
+ "logo": {
10081
+ "type": "VNode",
10082
+ "description": "Logo node above the heading."
10083
+ },
10084
+ "footer": {
10085
+ "type": "VNode",
10086
+ "description": "Content under the fields, for example \"No account? Sign up\"."
10087
+ },
10088
+ "image": {
10089
+ "type": "string",
10090
+ "description": "URL of the media image. Without it the panel shows an accent gradient."
10091
+ },
10092
+ "headline": {
10093
+ "type": "string",
10094
+ "description": "Large text on the media panel (photo and tint)."
10095
+ },
10096
+ "caption": {
10097
+ "type": "string",
10098
+ "description": "Small text on the media panel (photo) or inside the glass bar (light, tint)."
10099
+ },
10100
+ "quote": {
10101
+ "type": "string",
10102
+ "description": "Line at the bottom of the media panel; photo only."
10103
+ },
10104
+ "chips": {
10105
+ "type": "string[]",
10106
+ "description": "Pill labels inside the glass bar; light and tint."
10107
+ },
10108
+ "people": {
10109
+ "type": "{ name: string; src?: string }[]",
10110
+ "description": "Avatars: floating on the photo, stacked in the proof pill."
10111
+ },
10112
+ "proof": {
10113
+ "type": "string",
10114
+ "description": "Social-proof title, e.g. \"Join 20k+ users\"; light and tint. Shows the pill under the form."
10115
+ },
10116
+ "proofHint": {
10117
+ "type": "string",
10118
+ "description": "Second line of the proof pill."
10119
+ }
10120
+ },
10121
+ "slots": {
10122
+ "default": "Form fields and buttons. React passes them as children."
10123
+ },
10124
+ "notes": [
10125
+ "logo, footer, image, people are props in both Vue and React. The media panel is hidden below the form on narrow screens (stacks under it)."
10126
+ ]
10127
+ },
10128
+ {
10129
+ "name": "SocialAuth",
10130
+ "tag": "UiSocialAuth",
10131
+ "group": "domain",
10132
+ "description": "Continue with Google and Apple buttons with an \"or\" divider. Place it under the sign-in form.",
10133
+ "props": {
10134
+ "providers": {
10135
+ "type": "('google' | 'apple')[]",
10136
+ "default": "['google', 'apple']",
10137
+ "description": "Which buttons to show, in order."
10138
+ },
10139
+ "variant": {
10140
+ "type": "'stack' | 'icons'",
10141
+ "values": [
10142
+ "stack",
10143
+ "icons"
10144
+ ],
10145
+ "default": "stack",
10146
+ "description": "'stack': full-width buttons with a label, one per row. 'icons': a row of square icon buttons."
10147
+ },
10148
+ "divider": {
10149
+ "type": "boolean",
10150
+ "default": "true",
10151
+ "description": "Show the \"or\" line above the buttons."
10152
+ },
10153
+ "labels": {
10154
+ "type": "{ google?: string; apple?: string }",
10155
+ "description": "Provider names inserted into \"Continue with {provider}\"."
10156
+ },
10157
+ "onSelect": {
10158
+ "type": "(provider: 'google' | 'apple') => void",
10159
+ "description": "Called with the clicked provider. Start your OAuth flow here."
10160
+ },
10161
+ "size": {
10162
+ "type": "'xs' | 'sm' | 'md' | 'lg' | 'xl'",
10163
+ "values": [
10164
+ "xs",
10165
+ "sm",
10166
+ "md",
10167
+ "lg",
10168
+ "xl"
10169
+ ],
10170
+ "default": "md",
10171
+ "description": "Scales padding, text and avatars."
10172
+ }
10173
+ },
10174
+ "events": {
10175
+ "select": "A provider button was clicked; payload is \"google\" or \"apple\"."
10176
+ },
10177
+ "notes": [
10178
+ "The kit only renders the buttons. The OAuth flow itself is yours."
10179
+ ]
10180
+ },
10053
10181
  {
10054
10182
  "name": "PageDashboard",
10055
10183
  "tag": "UiPageDashboard",
@@ -10548,13 +10676,56 @@
10548
10676
  "props": {
10549
10677
  "layout": {
10550
10678
  "forwarded": true,
10551
- "type": "'card' | 'split'",
10679
+ "type": "'card' | 'split' | 'photo' | 'light' | 'tint'",
10552
10680
  "values": [
10553
10681
  "card",
10554
- "split"
10682
+ "split",
10683
+ "photo",
10684
+ "light",
10685
+ "tint"
10555
10686
  ],
10556
10687
  "default": "card",
10557
- "description": "'split' uses AuthSplit with a side panel; anything else renders a centered AuthLayout card over the background."
10688
+ "description": "'split' uses AuthSplit with a side panel. 'photo', 'light' and 'tint' use AuthShowcase with a media panel. Anything else renders a centered AuthLayout card over the background."
10689
+ },
10690
+ "image": {
10691
+ "forwarded": true,
10692
+ "type": "string",
10693
+ "description": "Media image URL; photo, light and tint layouts."
10694
+ },
10695
+ "headline": {
10696
+ "forwarded": true,
10697
+ "type": "string",
10698
+ "description": "Large text on the media panel; photo and tint."
10699
+ },
10700
+ "caption": {
10701
+ "forwarded": true,
10702
+ "type": "string",
10703
+ "description": "Small text on the media panel or glass bar."
10704
+ },
10705
+ "quote": {
10706
+ "forwarded": true,
10707
+ "type": "string",
10708
+ "description": "Bottom line of the media panel; photo only."
10709
+ },
10710
+ "chips": {
10711
+ "forwarded": true,
10712
+ "type": "string[]",
10713
+ "description": "Pill labels in the glass bar; light and tint."
10714
+ },
10715
+ "people": {
10716
+ "forwarded": true,
10717
+ "type": "{ name: string; src?: string }[]",
10718
+ "description": "Avatars on the media panel or in the proof pill."
10719
+ },
10720
+ "proof": {
10721
+ "forwarded": true,
10722
+ "type": "string",
10723
+ "description": "Social-proof title under the form; light and tint."
10724
+ },
10725
+ "proofHint": {
10726
+ "forwarded": true,
10727
+ "type": "string",
10728
+ "description": "Second line of the proof pill."
10558
10729
  },
10559
10730
  "title": {
10560
10731
  "forwarded": true,
package/dist/style.css CHANGED
@@ -494,6 +494,52 @@
494
494
  /* CommandPalette active row, dialog focus */
495
495
  .zg-menu__i.is-active,.zg-menu__i[aria-selected="true"]{background:var(--zg-s2)}
496
496
  .zg-modal:focus,.zg-sheet:focus,.zg-fsd:focus,.zg-saver:focus{outline:0}
497
+ /* AuthShowcase: photo (dark framed card), light (gradient card + proof pill + glass caption), tint (coloured panel, pill fields) */
498
+ .zg-ash{min-height:100%;padding:clamp(12px,3vw,40px);display:grid;place-items:center;--ash-r:28px;--ash-acc:var(--zg-brand)}
499
+ .zg-ash--tint{--ash-acc:oklch(.7 .13 215)}
500
+ .zg-ash__title{margin:0;font:300 clamp(28px,3.4vw,40px)/1.1 var(--zg-font);letter-spacing:-.03em}
501
+ .zg-ash__sub{margin:10px 0 0;color:var(--zg-text-2);line-height:1.55}
502
+ .zg-ash__head{display:flex;flex-direction:column;gap:6px}
503
+ .zg-ash__fields{display:flex;flex-direction:column;gap:14px;margin-top:22px}
504
+ .zg-ash__foot{margin-top:18px;color:var(--zg-text-2);font-size:14px;text-align:center}
505
+ .zg-ash__form{width:100%;max-width:400px;margin-inline:auto}
506
+ .zg-ash__media{position:relative;overflow:hidden;background:linear-gradient(150deg,color-mix(in oklch,var(--ash-acc) 55%,var(--zg-s2)),color-mix(in oklch,var(--ash-acc) 15%,var(--zg-s1)));background-size:cover;background-position:center}
507
+ .zg-ash--photo .zg-ash__frame{width:100%;max-width:1160px;display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);border-radius:var(--ash-r);overflow:hidden;background:var(--zg-s1);box-shadow:var(--zg-rim),var(--zg-shadow-1);min-height:560px}
508
+ .zg-ash--photo .zg-ash__media::before{content:"";position:absolute;inset:0;background:linear-gradient(90deg,color-mix(in oklch,var(--zg-bg) 15%,transparent),var(--zg-s1) 100%),linear-gradient(0deg,color-mix(in oklch,var(--zg-bg) 70%,transparent),transparent 55%)}
509
+ .zg-ash__copy{position:relative;z-index:1;padding:clamp(24px,5vw,64px);padding-top:clamp(60px,12vw,150px);max-width:480px}
510
+ .zg-ash__copy h2{margin:0 0 14px;font:500 clamp(30px,4vw,46px)/1.08 var(--zg-font);letter-spacing:-.03em}
511
+ .zg-ash__copy p{margin:0;color:var(--zg-text-2);font-size:18px;line-height:1.5}
512
+ .zg-ash__float{position:absolute;z-index:1;border-radius:50%;box-shadow:0 0 28px color-mix(in oklch,var(--zg-brand) 55%,transparent)}
513
+ .zg-ash__float--a{inset-inline-start:7%;top:60%}.zg-ash__float--b{inset-inline-start:52%;top:50%}.zg-ash__float--c{inset-inline-start:36%;top:76%}
514
+ @media(prefers-reduced-motion:no-preference){.zg-ash__float{animation:zg-ash-bob 6s ease-in-out infinite alternate}.zg-ash__float--b{animation-delay:-2s}.zg-ash__float--c{animation-delay:-4s}}
515
+ @keyframes zg-ash-bob{to{transform:translateY(-10px)}}
516
+ .zg-ash__quote{position:absolute;z-index:1;inset-inline-start:clamp(24px,5vw,64px);bottom:28px;color:var(--zg-text-1)}
517
+ .zg-ash--photo .zg-ash__side{display:grid;place-items:center;padding:clamp(24px,5vw,56px)}
518
+ .zg-ash__grid{width:100%;max-width:1180px;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:16px}
519
+ .zg-ash__col{display:flex;flex-direction:column;gap:16px;min-width:0}
520
+ .zg-ash__card{flex:1;display:grid;align-content:center;padding:clamp(24px,4vw,48px);border-radius:var(--ash-r);background:linear-gradient(165deg,var(--zg-s1) 40%,color-mix(in oklch,var(--ash-acc) 26%,var(--zg-s1)));box-shadow:var(--zg-rim)}
521
+ .zg-ash--light .zg-ash__head,.zg-ash--tint .zg-ash__head{align-items:center;text-align:center}
522
+ .zg-ash--tint .zg-ash__card{background:var(--zg-s1)}
523
+ .zg-ash--tint .zg-ash__title{font-weight:400}
524
+ .zg-ash__proof{display:flex;align-items:center;gap:14px;padding:14px 18px;border-radius:var(--ash-r);background:var(--zg-s1);box-shadow:var(--zg-rim)}
525
+ .zg-ash__stack{display:inline-flex}.zg-ash__stack .zg-avatar{margin-inline-end:-10px;box-shadow:0 0 0 2px var(--zg-s1)}
526
+ .zg-ash__proofT{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;margin-inline-start:10px}.zg-ash__proofT span{color:var(--zg-text-3);font-size:13px}
527
+ .zg-ash--light .zg-ash__grid>.zg-ash__media,.zg-ash--tint .zg-ash__grid>.zg-ash__media{border-radius:var(--ash-r);min-height:520px;display:flex;flex-direction:column;justify-content:flex-end;box-shadow:var(--zg-rim)}
528
+ .zg-ash__mh{position:absolute;inset-inline:0;top:0;margin:0;padding:clamp(20px,3vw,36px);font:400 clamp(24px,2.8vw,34px)/1.2 var(--zg-font);color:#fff;text-shadow:0 1px 16px rgb(0 0 0/.25)}
529
+ .zg-ash__glass{position:relative;margin:12px;padding:18px;border-radius:calc(var(--ash-r) - 8px);display:flex;flex-direction:column;gap:12px;background:color-mix(in oklch,var(--zg-bg) 38%,transparent);backdrop-filter:blur(22px) saturate(1.4);-webkit-backdrop-filter:blur(22px) saturate(1.4);box-shadow:var(--zg-rim);color:var(--zg-text-1)}
530
+ .zg-ash__glass p{margin:0;color:var(--zg-text-2)}
531
+ .zg-ash__chips{display:flex;gap:10px;flex-wrap:wrap}
532
+ .zg-ash__chip{flex:1;text-align:center;padding:14px 18px;border-radius:999px;box-shadow:inset 0 0 0 1px color-mix(in oklch,var(--zg-text-1) 40%,transparent)}
533
+ @media(max-width:860px){.zg-ash--photo .zg-ash__frame,.zg-ash__grid{grid-template-columns:1fr}.zg-ash--photo .zg-ash__media{min-height:300px}.zg-ash__grid>.zg-ash__media{min-height:320px!important;order:2}}
534
+ /* SocialAuth */
535
+ .zg-social{display:flex;flex-direction:column;gap:14px}
536
+ .zg-social__or{display:flex;align-items:center;gap:12px;color:var(--zg-text-3);font-size:13px}.zg-social__or::before,.zg-social__or::after{content:"";flex:1;height:1px;background:var(--zg-stroke)}
537
+ .zg-social__row{display:flex;gap:10px}.zg-social--stack .zg-social__row{flex-direction:column}
538
+ .zg-social .zg-btn{gap:10px}.zg-social:not(.zg-social--stack) .zg-social__row{justify-content:center}
539
+ /* default media art when no image is given */
540
+ .zg-ash__media::after{content:"";position:absolute;inset:0;pointer-events:none;background:radial-gradient(circle at 78% 24%,color-mix(in oklch,#fff 55%,transparent) 0,transparent 30%),radial-gradient(circle at 18% 78%,color-mix(in oklch,var(--ash-acc) 70%,transparent) 0,transparent 38%),conic-gradient(from 210deg at 62% 48%,transparent 0 .08turn,color-mix(in oklch,#fff 22%,transparent) .1turn .18turn,transparent .2turn 1turn)}
541
+ .zg-ash__media[style*="url("]::after{display:none}
542
+ .zg-ash--light .zg-ash__media{--ash-acc:oklch(.72 .08 80)}
497
543
 
498
544
 
499
545
  /* ./sizes/typography.css */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ziyarago/ui",
3
- "version": "0.4.0",
4
- "description": "ZiyaraGo UI — glass design system for Umrah and Hajj products. OKLCH tokens, two palettes, dark and light, RTL, Thmanyah. One framework-neutral core with Vue 3, Nuxt and React adapters, 182 components and 17 page templates.",
3
+ "version": "0.6.0",
4
+ "description": "ZiyaraGo UI — glass design system for Umrah and Hajj products. OKLCH tokens, two palettes, dark and light, RTL, Thmanyah. One framework-neutral core with Vue 3, Nuxt and React adapters, 184 components and 17 page templates.",
5
5
  "keywords": [
6
6
  "ui",
7
7
  "design-system",
package/src/core/bools.js CHANGED
@@ -1,4 +1,4 @@
1
1
  // Names of props that are boolean in EVERY component that documents them. The Vue adapter turns the bare-attribute form (<UiButton block>, which Vue delivers as '') into true for these.
2
2
  // Names that are boolean somewhere and text elsewhere (modelValue, image, brand) are not listed: '' is a legitimate value there.
3
3
  // scripts/check-meta.mjs keeps this list in sync with src/meta.
4
- export const BOOL_PROPS = new Set(['addable', 'allowEmpty', 'autoResize', 'autofocus', 'banner', 'block', 'bold', 'checked', 'clearable', 'closable', 'closeOnBackdrop', 'closeOnEsc', 'collapsed', 'collapsible', 'disabled', 'dot', 'external', 'featured', 'frame', 'ghost', 'glass', 'hideArrows', 'hideIcon', 'highlightLast', 'iconOnly', 'indeterminate', 'interactive', 'labelLeft', 'last', 'loading', 'multi', 'multiple', 'open', 'pill', 'plain', 'readonly', 'required', 'resize', 'ring', 'sanitize', 'selectGroups', 'showCount', 'showForgot', 'showValue', 'spellcheck', 'ticks', 'truncate', 'vertical', 'vip', 'wrap']);
4
+ export const BOOL_PROPS = new Set(['addable', 'allowEmpty', 'autoResize', 'autofocus', 'banner', 'block', 'bold', 'checked', 'clearable', 'closable', 'closeOnBackdrop', 'closeOnEsc', 'collapsed', 'collapsible', 'disabled', 'divider', 'dot', 'external', 'featured', 'frame', 'ghost', 'glass', 'hideArrows', 'hideIcon', 'highlightLast', 'iconOnly', 'indeterminate', 'interactive', 'labelLeft', 'last', 'loading', 'multi', 'multiple', 'open', 'pill', 'plain', 'readonly', 'required', 'resize', 'ring', 'sanitize', 'selectGroups', 'showCount', 'showForgot', 'showValue', 'spellcheck', 'ticks', 'truncate', 'vertical', 'vip', 'wrap']);
@@ -30,6 +30,42 @@ export const more3 = {
30
30
  Snackbar: { render: ({ h, props: p, children }) => h('div', { class: cx('zg-snack', p.variant === 'tonal' && 'zg-snack--tonal', tc(p.tone)), role: 'status' }, h('span', { style: { flex: 1 } }, ...children), p.action ? h('button', { type: 'button', onClick: p.onAction, style: { color: p.tone === 'success' ? 'var(--zg-green)' : 'var(--zg-red)' } }, p.action) : null, p.onClose ? h('button', { type: 'button', onClick: p.onClose, 'aria-label': t('Snackbar', 'close') }, icon(h, 'x', 14)) : null) },
31
31
  TourPopover: { render: ({ C, h, props: p }) => h('div', { class: 'zg-tour' }, h('div', { style: { display: 'flex', alignItems: 'center', gap: '8px' } }, h('b', { style: { flex: 1, fontSize: '16px' } }, p.title), h('button', { type: 'button', 'aria-label': t('TourPopover', 'close'), class: 'zg-ibtn zg-ibtn--sm zg-ibtn--ghost', onClick: p.onClose }, icon(h, 'x', 14))), h('span', { style: { fontSize: '14px', lineHeight: 1.5 } }, p.text), h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' } }, h('span', { class: 'zg-hint zg-mono' }, p.step || ''), h('div', { style: { display: 'flex', gap: '8px' } }, p.onBack ? h(C.Button, { size: 'sm', onClick: p.onBack }, t('TourPopover', 'back')) : null, h(C.Button, { size: 'sm', variant: 'primary', onClick: p.onNext }, p.last ? t('TourPopover', 'finish') : t('TourPopover', 'next'))))) },
32
32
  ProfileHeader: { render: ({ C, h, props: p, children }) => h('section', { class: 'zg-card zg-glass zg-prof' }, h('div', { class: 'zg-prof__cover', style: { backgroundImage: p.cover ? `url(${p.cover})` : undefined } }), h('div', { class: 'zg-prof__row' }, h('span', { class: 'zg-prof__av' }, h(C.Avatar, { name: p.name, src: p.src, size: 104 })), h('div', { style: { flex: 1, minWidth: '200px', display: 'flex', flexDirection: 'column', gap: '8px' } }, h('b', { style: { fontSize: '22px', fontWeight: 500 } }, p.name), h('div', { class: 'zg-chips', style: { color: 'var(--zg-text-3)', fontSize: '13px' } }, ...(p.meta || []).map(m => h('span', { key: m.label, style: { display: 'inline-flex', gap: '6px', alignItems: 'center' } }, icon(h, m.icon, 14), m.label)))), ...children)) },
33
+ SocialAuth: { render: ({ C, h, props: p }) => {
34
+ const list = (p.providers || ['google', 'apple']).filter(k => k === 'google' || k === 'apple');
35
+ const icon = k => k === 'apple'
36
+ ? h('svg', { viewBox: '0 0 24 24', width: 20, height: 20, 'aria-hidden': 'true', fill: 'currentColor' }, h('path', { d: 'M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701' }))
37
+ : h('svg', { viewBox: '0 0 24 24', width: 20, height: 20, 'aria-hidden': 'true' },
38
+ h('path', { fill: '#4285F4', d: 'M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z' }),
39
+ h('path', { fill: '#34A853', d: 'M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z' }),
40
+ h('path', { fill: '#FBBC05', d: 'M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z' }),
41
+ h('path', { fill: '#EA4335', d: 'M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z' }));
42
+ const name = k => (p.labels && p.labels[k]) || t('SocialAuth', k);
43
+ const stack = p.variant !== 'icons';
44
+ return h('div', { class: 'zg-social' + (stack ? ' zg-social--stack' : '') },
45
+ p.divider === false ? null : h('div', { class: 'zg-social__or', role: 'separator' }, h('span', null, t('SocialAuth', 'or'))),
46
+ h('div', { class: 'zg-social__row' }, ...list.map(k => h(C.Button, { key: k, variant: 'secondary', size: 'lg', block: stack, iconOnly: !stack, title: t('SocialAuth', 'continueWith', { provider: name(k) }), 'aria-label': t('SocialAuth', 'continueWith', { provider: name(k) }), onClick: () => p.onSelect && p.onSelect(k) }, icon(k), stack ? t('SocialAuth', 'continueWith', { provider: name(k) }) : null))));
47
+ } },
48
+ AuthShowcase: { render: ({ C, h, props: p, children }) => {
49
+ const v = p.variant === 'light' || p.variant === 'tint' ? p.variant : 'photo';
50
+ const people = p.people || [];
51
+ const stack = size => h('span', { class: 'zg-ash__stack' }, ...people.slice(0, 4).map((u, i) => h(C.Avatar, { key: i, name: u.name, src: u.src, size })));
52
+ const head = h('div', { class: 'zg-ash__head' }, p.logo || null, h('h1', { class: 'zg-ash__title' }, p.title), p.subtitle ? h('p', { class: 'zg-ash__sub' }, p.subtitle) : null);
53
+ const form = h('div', { class: 'zg-ash__form' }, head, h('div', { class: 'zg-ash__fields' }, ...children), p.footer ? h('div', { class: 'zg-ash__foot' }, p.footer) : null);
54
+ const art = { backgroundImage: p.image ? 'url(' + p.image + ')' : undefined };
55
+ if (v === 'photo') {
56
+ const spots = ['a', 'b', 'c'];
57
+ return h('div', { class: 'zg-root zg-ash zg-ash--photo' }, h('div', { class: 'zg-ash__frame' },
58
+ h('section', { class: 'zg-ash__media', style: art }, h('div', { class: 'zg-ash__copy' }, p.headline ? h('h2', null, p.headline) : null, p.caption ? h('p', null, p.caption) : null),
59
+ ...people.slice(0, 3).map((u, i) => h('span', { key: i, class: 'zg-ash__float zg-ash__float--' + spots[i] }, h(C.Avatar, { name: u.name, src: u.src, size: i === 1 ? 'lg' : 'md' }))),
60
+ p.quote ? h('small', { class: 'zg-ash__quote' }, p.quote) : null),
61
+ h('main', { class: 'zg-ash__side' }, form)));
62
+ }
63
+ return h('div', { class: 'zg-root zg-ash zg-ash--' + v }, h('div', { class: 'zg-ash__grid' },
64
+ h('div', { class: 'zg-ash__col' }, h('main', { class: 'zg-ash__card' }, form),
65
+ p.proof ? h('div', { class: 'zg-ash__proof' }, stack('sm'), h('div', { class: 'zg-ash__proofT' }, h('b', null, p.proof), p.proofHint ? h('span', null, p.proofHint) : null), h(C.IconButton, { icon: 'arrow-up-right', label: p.proof, ghost: true })) : null),
66
+ h('section', { class: 'zg-ash__media', style: art }, v === 'tint' && p.headline ? h('h2', { class: 'zg-ash__mh' }, p.headline) : null,
67
+ h('div', { class: 'zg-ash__glass' }, (p.chips || []).length ? h('div', { class: 'zg-ash__chips' }, ...p.chips.map((c, i) => h('span', { key: i, class: 'zg-ash__chip' }, c))) : null, p.caption ? h('p', null, p.caption) : null))));
68
+ } },
33
69
  AuthSplit: { render: ({ h, props: p, children }) => h('div', { class: 'zg-root zg-split2' }, h('aside', { class: 'zg-split2__a' }, p.aside), h('main', { class: 'zg-split2__m' }, h('div', { style: { width: '100%', maxWidth: '420px', display: 'flex', flexDirection: 'column', gap: '20px' } }, p.logo || null, h('div', null, h('h1', { style: { margin: 0, font: '300 28px var(--zg-font)', letterSpacing: '-.03em' } }, p.title), p.subtitle ? h('p', { style: { margin: '6px 0 0', color: 'var(--zg-text-2)', lineHeight: 1.5 } }, p.subtitle) : null), ...children))) },
34
70
  StatusPage: { state: { email: '' }, render: ({ C, h, props: p, children, state, set }) => { const K = { 404: ['404', t('StatusPage', 'nfTitle'), t('StatusPage', 'nfDesc'), t('StatusPage', 'home')], 401: ['401', t('StatusPage', 'authTitle'), t('StatusPage', 'authDesc'), t('StatusPage', 'home')], maintenance: ['', t('StatusPage', 'mtTitle'), t('StatusPage', 'mtDesc'), t('StatusPage', 'home')], soon: ['', t('StatusPage', 'soonTitle'), t('StatusPage', 'soonDesc'), t('StatusPage', 'notify')] }[p.kind || '404'];
35
71
  return h('div', { class: 'zg-es zg-es--page' }, p.codeNode || (K[0] ? h('span', { class: 'zg-num', style: { fontSize: 'clamp(80px,14vw,150px)', lineHeight: 1, color: 'var(--zg-brand-text)' } }, K[0]) : h('span', { class: 'zg-es__ico' }, icon(h, p.kind === 'soon' ? 'rocket' : 'wrench', 28))), p.messageNode || [h('b', { key: 't', style: { fontSize: '24px' } }, p.title || K[1]), h('span', { key: 'd' }, p.description || K[2])], p.actionNode || (p.kind === 'soon' ? h('div', { style: { display: 'flex', gap: '8px', flexWrap: 'wrap', justifyContent: 'center', width: '100%', maxWidth: '420px' } }, h('div', { style: { flex: '1 1 200px' } }, h(C.TextField, { placeholder: p.emailLabel || 'name@email.com', icon: 'mail', modelValue: state.email, onChange: v => set({ email: v }) })), h(C.Button, { variant: 'primary', onClick: () => p.onNotify && p.onNotify(state.email) }, p.notifyLabel || K[3])) : h(C.Button, { variant: 'primary', onClick: p.onAction }, p.actionLabel || K[3])), ...children); } }
package/src/core/pages.js CHANGED
@@ -42,6 +42,7 @@ const T = Object.fromEntries([
42
42
 
43
43
  T.PageAuth = { slots: ['background', 'logo', 'aside', 'form', 'footer'], render: ({ C, h, props: p, slots: s }) => {
44
44
  const body = [s.form || null, s.footer ? h('div', { class: 'zg-pg__r', 'data-slot': 'footer' }, s.footer) : null];
45
+ if (['photo', 'light', 'tint'].includes(p.layout)) return h(C.AuthShowcase, { variant: p.layout, title: p.title, subtitle: p.subtitle, logo: s.logo, footer: s.footer, image: p.image, headline: p.headline, caption: p.caption, quote: p.quote, chips: p.chips, people: p.people, proof: p.proof, proofHint: p.proofHint }, s.form || null);
45
46
  return p.layout === 'split'
46
47
  ? h(C.AuthSplit, { title: p.title, subtitle: p.subtitle, logo: s.logo, aside: s.aside }, ...body)
47
48
  : h('div', { class: 'zg-pgauth' }, s.background ? h('div', { class: 'zg-pg__r zg-pgauth__bg', 'data-slot': 'background' }, s.background) : null, h('div', { class: 'zg-pgauth__c' }, h(C.AuthLayout, { title: p.title, subtitle: p.subtitle, logo: s.logo }, ...body)));
@@ -0,0 +1,5 @@
1
+ export default {
2
+ en: { or: 'or', continueWith: 'Continue with {provider}', google: 'Google', apple: 'Apple' },
3
+ ar: { or: 'أو', continueWith: 'المتابعة باستخدام {provider}', google: 'Google', apple: 'Apple' },
4
+ ru: { or: 'или', continueWith: 'Продолжить с {provider}', google: 'Google', apple: 'Apple' },
5
+ };
@@ -53,6 +53,7 @@ import Screensaver from './Screensaver.js';
53
53
  import SearchInput from './SearchInput.js';
54
54
  import SideNav from './SideNav.js';
55
55
  import Snackbar from './Snackbar.js';
56
+ import SocialAuth from './SocialAuth.js';
56
57
  import Sortable from './Sortable.js';
57
58
  import Spinner from './Spinner.js';
58
59
  import StatusPage from './StatusPage.js';
@@ -120,6 +121,7 @@ defineMessages('Screensaver', Screensaver);
120
121
  defineMessages('SearchInput', SearchInput);
121
122
  defineMessages('SideNav', SideNav);
122
123
  defineMessages('Snackbar', Snackbar);
124
+ defineMessages('SocialAuth', SocialAuth);
123
125
  defineMessages('Sortable', Sortable);
124
126
  defineMessages('Spinner', Spinner);
125
127
  defineMessages('StatusPage', StatusPage);
@@ -198,6 +198,39 @@ export default {
198
198
  },
199
199
  events: { decline: 'Decline button clicked.', answer: 'Answer button clicked.' },
200
200
  },
201
+ SocialAuth: {
202
+ description: 'Continue with Google and Apple buttons with an "or" divider. Place it under the sign-in form.',
203
+ props: {
204
+ providers: { type: "('google' | 'apple')[]", default: "['google', 'apple']", description: 'Which buttons to show, in order.' },
205
+ variant: { type: "'stack' | 'icons'", values: ['stack', 'icons'], default: 'stack', description: "'stack': full-width buttons with a label, one per row. 'icons': a row of square icon buttons." },
206
+ divider: { type: 'boolean', default: 'true', description: 'Show the "or" line above the buttons.' },
207
+ labels: { type: '{ google?: string; apple?: string }', description: 'Provider names inserted into "Continue with {provider}".' },
208
+ onSelect: { type: "(provider: 'google' | 'apple') => void", description: 'Called with the clicked provider. Start your OAuth flow here.' },
209
+ size,
210
+ },
211
+ events: { select: 'A provider button was clicked; payload is "google" or "apple".' },
212
+ notes: ['The kit only renders the buttons. The OAuth flow itself is yours.'],
213
+ },
214
+ AuthShowcase: {
215
+ description: 'Sign-in layout with a media panel: dark photo card with floating avatars, light gradient card with a social-proof pill, or a tinted panel with a glass caption. The default slot is the form.',
216
+ props: {
217
+ variant: { type: "'photo' | 'light' | 'tint'", values: ['photo', 'light', 'tint'], default: 'photo', description: "'photo': one framed card, media on the left. 'light': gradient form card, proof pill below, glass caption on the media. 'tint': like light, with a coloured panel, a heading on the media and cyan accent." },
218
+ title: { type: 'string', required: true, description: 'Form heading.' },
219
+ subtitle: { type: 'string', description: 'Text under the heading.' },
220
+ logo: { type: 'VNode', description: 'Logo node above the heading.' },
221
+ footer: { type: 'VNode', description: 'Content under the fields, for example "No account? Sign up".' },
222
+ image: { type: 'string', description: 'URL of the media image. Without it the panel shows an accent gradient.' },
223
+ headline: { type: 'string', description: 'Large text on the media panel (photo and tint).' },
224
+ caption: { type: 'string', description: 'Small text on the media panel (photo) or inside the glass bar (light, tint).' },
225
+ quote: { type: 'string', description: 'Line at the bottom of the media panel; photo only.' },
226
+ chips: { type: 'string[]', description: 'Pill labels inside the glass bar; light and tint.' },
227
+ people: { type: '{ name: string; src?: string }[]', description: 'Avatars: floating on the photo, stacked in the proof pill.' },
228
+ proof: { type: 'string', description: 'Social-proof title, e.g. "Join 20k+ users"; light and tint. Shows the pill under the form.' },
229
+ proofHint: { type: 'string', description: 'Second line of the proof pill.' },
230
+ },
231
+ slots: { default: 'Form fields and buttons. React passes them as children.' },
232
+ notes: ['logo, footer, image, people are props in both Vue and React. The media panel is hidden below the form on narrow screens (stacks under it).'],
233
+ },
201
234
  AuthSplit: {
202
235
  description: 'Two-column sign-in layout with a brand panel on the left and the form on the right.',
203
236
  props: {
@@ -9,7 +9,7 @@ export const GROUPS = [
9
9
  { id: 'overlay', title: 'Overlay', blurb: 'Dialogs, sheets and popovers.', names: ['Modal', 'Drawer', 'ConfirmDialog', 'Popconfirm', 'StepDialog', 'FullscreenDialog', 'BottomSheet', 'Actionsheet', 'Popover', 'HoverCard', 'Tooltip', 'Dropdown', 'Menu', 'ContextMenu', 'CommandBar', 'CommandPalette', 'ImageViewer', 'Tour', 'TourPopover', 'Screensaver'] },
10
10
  { id: 'navigation', title: 'Navigation', blurb: 'Menus, tabs and paging.', names: ['TopNav', 'SideNav', 'SubNav', 'Tabs', 'PillTabs', 'Breadcrumbs', 'Pagination', 'Accordion'] },
11
11
  { id: 'media', title: 'Media and icons', blurb: 'Avatars, images, maps and icons.', names: ['Avatar', 'Icon', 'LazyImage', 'Gallery', 'Carousel', 'Map', 'ProfileHeader', 'ChatAi'] },
12
- { id: 'domain', title: 'Domain', blurb: 'Umrah, Hajj, hotel and CRM blocks.', names: ['HotelCard', 'TourCard', 'PrayerTimes', 'RamadanTracker', 'BookingCard', 'GuestCard', 'RoomGrid', 'RoomCalendar', 'LeadCard', 'DealCard', 'PipelineBoard', 'ContactPanel', 'KanbanColumn', 'KanbanCard', 'CallRow', 'CallCard', 'IncomingCall', 'AuthSplit'] },
12
+ { id: 'domain', title: 'Domain', blurb: 'Umrah, Hajj, hotel and CRM blocks.', names: ['HotelCard', 'TourCard', 'PrayerTimes', 'RamadanTracker', 'BookingCard', 'GuestCard', 'RoomGrid', 'RoomCalendar', 'LeadCard', 'DealCard', 'PipelineBoard', 'ContactPanel', 'KanbanColumn', 'KanbanCard', 'CallRow', 'CallCard', 'IncomingCall', 'AuthSplit', 'AuthShowcase', 'SocialAuth'] },
13
13
  { id: 'pages', title: 'Page templates', blurb: 'Ready layouts with named slots.', names: ['PageDashboard', 'PagePatterns', 'PageCrm', 'PageBookings', 'PageCatalog', 'PageDetail', 'PageSettings', 'PageUserList', 'PageProfile', 'PageForm', 'PageOrder', 'PageAccount', 'PagePricing', 'PageFaq', 'PageAuth', 'PageStatus'] },
14
14
  ];
15
15
 
package/src/meta/pages.js CHANGED
@@ -194,7 +194,15 @@ export default {
194
194
  PageAuth: {
195
195
  description: 'Sign-in page template without the app shell, as a centered card or a split layout.',
196
196
  props: {
197
- layout: { forwarded: true, type: "'card' | 'split'", values: ['card', 'split'], default: 'card', description: "'split' uses AuthSplit with a side panel; anything else renders a centered AuthLayout card over the background." },
197
+ layout: { forwarded: true, type: "'card' | 'split' | 'photo' | 'light' | 'tint'", values: ['card', 'split', 'photo', 'light', 'tint'], default: 'card', description: "'split' uses AuthSplit with a side panel. 'photo', 'light' and 'tint' use AuthShowcase with a media panel. Anything else renders a centered AuthLayout card over the background." },
198
+ image: { forwarded: true, type: 'string', description: 'Media image URL; photo, light and tint layouts.' },
199
+ headline: { forwarded: true, type: 'string', description: 'Large text on the media panel; photo and tint.' },
200
+ caption: { forwarded: true, type: 'string', description: 'Small text on the media panel or glass bar.' },
201
+ quote: { forwarded: true, type: 'string', description: 'Bottom line of the media panel; photo only.' },
202
+ chips: { forwarded: true, type: 'string[]', description: 'Pill labels in the glass bar; light and tint.' },
203
+ people: { forwarded: true, type: '{ name: string; src?: string }[]', description: 'Avatars on the media panel or in the proof pill.' },
204
+ proof: { forwarded: true, type: 'string', description: 'Social-proof title under the form; light and tint.' },
205
+ proofHint: { forwarded: true, type: 'string', description: 'Second line of the proof pill.' },
198
206
  title: { forwarded: true, type: 'string', description: 'Form heading.' },
199
207
  subtitle: { forwarded: true, type: 'string', description: 'Text under the heading.' },
200
208
  },
@@ -2030,7 +2030,7 @@
2030
2030
  "route": "auth",
2031
2031
  "label": "Authentication (6)",
2032
2032
  "icon": "lock-keyhole",
2033
- "desc": "Six sign-in screens in two layouts: centered card and split with a side panel. Always dark theme.",
2033
+ "desc": "Seven sign-in screens in five layouts: centered card, split with a side panel, and three with a media panel (photo, light, tint).",
2034
2034
  "component": "PageAuth",
2035
2035
  "tpl": "TplAuth",
2036
2036
  "shell": false,
@@ -171,6 +171,8 @@ export const UiSnackbar = C.Snackbar;
171
171
  export const UiTourPopover = C.TourPopover;
172
172
  export const UiProfileHeader = C.ProfileHeader;
173
173
  export const UiAuthSplit = C.AuthSplit;
174
+ export const UiAuthShowcase = C.AuthShowcase;
175
+ export const UiSocialAuth = C.SocialAuth;
174
176
  export const UiStatusPage = C.StatusPage;
175
177
  export const UiValidatedField = C.ValidatedField;
176
178
  export const UiFooter = C.Footer;
@@ -45,3 +45,49 @@
45
45
  /* CommandPalette active row, dialog focus */
46
46
  .zg-menu__i.is-active,.zg-menu__i[aria-selected="true"]{background:var(--zg-s2)}
47
47
  .zg-modal:focus,.zg-sheet:focus,.zg-fsd:focus,.zg-saver:focus{outline:0}
48
+ /* AuthShowcase: photo (dark framed card), light (gradient card + proof pill + glass caption), tint (coloured panel, pill fields) */
49
+ .zg-ash{min-height:100%;padding:clamp(12px,3vw,40px);display:grid;place-items:center;--ash-r:28px;--ash-acc:var(--zg-brand)}
50
+ .zg-ash--tint{--ash-acc:oklch(.7 .13 215)}
51
+ .zg-ash__title{margin:0;font:300 clamp(28px,3.4vw,40px)/1.1 var(--zg-font);letter-spacing:-.03em}
52
+ .zg-ash__sub{margin:10px 0 0;color:var(--zg-text-2);line-height:1.55}
53
+ .zg-ash__head{display:flex;flex-direction:column;gap:6px}
54
+ .zg-ash__fields{display:flex;flex-direction:column;gap:14px;margin-top:22px}
55
+ .zg-ash__foot{margin-top:18px;color:var(--zg-text-2);font-size:14px;text-align:center}
56
+ .zg-ash__form{width:100%;max-width:400px;margin-inline:auto}
57
+ .zg-ash__media{position:relative;overflow:hidden;background:linear-gradient(150deg,color-mix(in oklch,var(--ash-acc) 55%,var(--zg-s2)),color-mix(in oklch,var(--ash-acc) 15%,var(--zg-s1)));background-size:cover;background-position:center}
58
+ .zg-ash--photo .zg-ash__frame{width:100%;max-width:1160px;display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);border-radius:var(--ash-r);overflow:hidden;background:var(--zg-s1);box-shadow:var(--zg-rim),var(--zg-shadow-1);min-height:560px}
59
+ .zg-ash--photo .zg-ash__media::before{content:"";position:absolute;inset:0;background:linear-gradient(90deg,color-mix(in oklch,var(--zg-bg) 15%,transparent),var(--zg-s1) 100%),linear-gradient(0deg,color-mix(in oklch,var(--zg-bg) 70%,transparent),transparent 55%)}
60
+ .zg-ash__copy{position:relative;z-index:1;padding:clamp(24px,5vw,64px);padding-top:clamp(60px,12vw,150px);max-width:480px}
61
+ .zg-ash__copy h2{margin:0 0 14px;font:500 clamp(30px,4vw,46px)/1.08 var(--zg-font);letter-spacing:-.03em}
62
+ .zg-ash__copy p{margin:0;color:var(--zg-text-2);font-size:18px;line-height:1.5}
63
+ .zg-ash__float{position:absolute;z-index:1;border-radius:50%;box-shadow:0 0 28px color-mix(in oklch,var(--zg-brand) 55%,transparent)}
64
+ .zg-ash__float--a{inset-inline-start:7%;top:60%}.zg-ash__float--b{inset-inline-start:52%;top:50%}.zg-ash__float--c{inset-inline-start:36%;top:76%}
65
+ @media(prefers-reduced-motion:no-preference){.zg-ash__float{animation:zg-ash-bob 6s ease-in-out infinite alternate}.zg-ash__float--b{animation-delay:-2s}.zg-ash__float--c{animation-delay:-4s}}
66
+ @keyframes zg-ash-bob{to{transform:translateY(-10px)}}
67
+ .zg-ash__quote{position:absolute;z-index:1;inset-inline-start:clamp(24px,5vw,64px);bottom:28px;color:var(--zg-text-1)}
68
+ .zg-ash--photo .zg-ash__side{display:grid;place-items:center;padding:clamp(24px,5vw,56px)}
69
+ .zg-ash__grid{width:100%;max-width:1180px;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:16px}
70
+ .zg-ash__col{display:flex;flex-direction:column;gap:16px;min-width:0}
71
+ .zg-ash__card{flex:1;display:grid;align-content:center;padding:clamp(24px,4vw,48px);border-radius:var(--ash-r);background:linear-gradient(165deg,var(--zg-s1) 40%,color-mix(in oklch,var(--ash-acc) 26%,var(--zg-s1)));box-shadow:var(--zg-rim)}
72
+ .zg-ash--light .zg-ash__head,.zg-ash--tint .zg-ash__head{align-items:center;text-align:center}
73
+ .zg-ash--tint .zg-ash__card{background:var(--zg-s1)}
74
+ .zg-ash--tint .zg-ash__title{font-weight:400}
75
+ .zg-ash__proof{display:flex;align-items:center;gap:14px;padding:14px 18px;border-radius:var(--ash-r);background:var(--zg-s1);box-shadow:var(--zg-rim)}
76
+ .zg-ash__stack{display:inline-flex}.zg-ash__stack .zg-avatar{margin-inline-end:-10px;box-shadow:0 0 0 2px var(--zg-s1)}
77
+ .zg-ash__proofT{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;margin-inline-start:10px}.zg-ash__proofT span{color:var(--zg-text-3);font-size:13px}
78
+ .zg-ash--light .zg-ash__grid>.zg-ash__media,.zg-ash--tint .zg-ash__grid>.zg-ash__media{border-radius:var(--ash-r);min-height:520px;display:flex;flex-direction:column;justify-content:flex-end;box-shadow:var(--zg-rim)}
79
+ .zg-ash__mh{position:absolute;inset-inline:0;top:0;margin:0;padding:clamp(20px,3vw,36px);font:400 clamp(24px,2.8vw,34px)/1.2 var(--zg-font);color:#fff;text-shadow:0 1px 16px rgb(0 0 0/.25)}
80
+ .zg-ash__glass{position:relative;margin:12px;padding:18px;border-radius:calc(var(--ash-r) - 8px);display:flex;flex-direction:column;gap:12px;background:color-mix(in oklch,var(--zg-bg) 38%,transparent);backdrop-filter:blur(22px) saturate(1.4);-webkit-backdrop-filter:blur(22px) saturate(1.4);box-shadow:var(--zg-rim);color:var(--zg-text-1)}
81
+ .zg-ash__glass p{margin:0;color:var(--zg-text-2)}
82
+ .zg-ash__chips{display:flex;gap:10px;flex-wrap:wrap}
83
+ .zg-ash__chip{flex:1;text-align:center;padding:14px 18px;border-radius:999px;box-shadow:inset 0 0 0 1px color-mix(in oklch,var(--zg-text-1) 40%,transparent)}
84
+ @media(max-width:860px){.zg-ash--photo .zg-ash__frame,.zg-ash__grid{grid-template-columns:1fr}.zg-ash--photo .zg-ash__media{min-height:300px}.zg-ash__grid>.zg-ash__media{min-height:320px!important;order:2}}
85
+ /* SocialAuth */
86
+ .zg-social{display:flex;flex-direction:column;gap:14px}
87
+ .zg-social__or{display:flex;align-items:center;gap:12px;color:var(--zg-text-3);font-size:13px}.zg-social__or::before,.zg-social__or::after{content:"";flex:1;height:1px;background:var(--zg-stroke)}
88
+ .zg-social__row{display:flex;gap:10px}.zg-social--stack .zg-social__row{flex-direction:column}
89
+ .zg-social .zg-btn{gap:10px}.zg-social:not(.zg-social--stack) .zg-social__row{justify-content:center}
90
+ /* default media art when no image is given */
91
+ .zg-ash__media::after{content:"";position:absolute;inset:0;pointer-events:none;background:radial-gradient(circle at 78% 24%,color-mix(in oklch,#fff 55%,transparent) 0,transparent 30%),radial-gradient(circle at 18% 78%,color-mix(in oklch,var(--ash-acc) 70%,transparent) 0,transparent 38%),conic-gradient(from 210deg at 62% 48%,transparent 0 .08turn,color-mix(in oklch,#fff 22%,transparent) .1turn .18turn,transparent .2turn 1turn)}
92
+ .zg-ash__media[style*="url("]::after{display:none}
93
+ .zg-ash--light .zg-ash__media{--ash-acc:oklch(.72 .08 80)}
package/src/vue/index.js CHANGED
@@ -29,7 +29,7 @@ const wrap = (name, def) => defineComponent({
29
29
  });
30
30
  for (const [name, def] of Object.entries(components)) C[name] = wrap(name, def);
31
31
 
32
- export const { Button: UiButton, IconButton: UiIconButton, Badge: UiBadge, Avatar: UiAvatar, Switch: UiSwitch, Checkbox: UiCheckbox, SegmentedControl: UiSegmentedControl, Tabs: UiTabs, FilterChips: UiFilterChips, TextField: UiTextField, Select: UiSelect, CodeInput: UiCodeInput, Stepper: UiStepper, Card: UiCard, Modal: UiModal, Drawer: UiDrawer, Toast: UiToast, Breadcrumbs: UiBreadcrumbs, Pagination: UiPagination, Progress: UiProgress, Ring: UiRing, KpiTile: UiKpiTile, DataTable: UiDataTable, KanbanColumn: UiKanbanColumn, KanbanCard: UiKanbanCard, Timeline: UiTimeline, TopNav: UiTopNav, SideNav: UiSideNav, AppShell: UiAppShell, PageHeader: UiPageHeader, AuthLayout: UiAuthLayout, LeadCard: UiLeadCard, HotelCard: UiHotelCard, TourCard: UiTourCard, PrayerTimes: UiPrayerTimes, RamadanTracker: UiRamadanTracker, CallRow: UiCallRow, Alert: UiAlert, Banner: UiBanner, Accordion: UiAccordion, Skeleton: UiSkeleton, Spinner: UiSpinner, Divider: UiDivider, EmptyState: UiEmptyState, ErrorPage: UiErrorPage, DataState: UiDataState, LoadingOverlay: UiLoadingOverlay, ConfirmDialog: UiConfirmDialog, Popconfirm: UiPopconfirm, Carousel: UiCarousel, Gallery: UiGallery, Code: UiCode, PricingCard: UiPricingCard, IntegrationCard: UiIntegrationCard, IncomingCall: UiIncomingCall, CallCard: UiCallCard, Dropdown: UiDropdown, Menu: UiMenu, Tooltip: UiTooltip, HoverCard: UiHoverCard, Popover: UiPopover, CopyButton: UiCopyButton, CommandBar: UiCommandBar, CommandPalette: UiCommandPalette, NotificationBell: UiNotificationBell, ToggleGroup: UiToggleGroup, Fab: UiFab, Textarea: UiTextarea, NumberInput: UiNumberInput, PasswordInput: UiPasswordInput, SearchInput: UiSearchInput, RadioGroup: UiRadioGroup, Slider: UiSlider, Rating: UiRating, ChipInput: UiChipInput, InlineEdit: UiInlineEdit, ColorPicker: UiColorPicker, FileUpload: UiFileUpload, Combobox: UiCombobox, MultiSelect: UiMultiSelect, GroupedSelect: UiGroupedSelect, PhoneInput: UiPhoneInput, Calendar: UiCalendar, DatePicker: UiDatePicker, TimePicker: UiTimePicker, FormField: UiFormField, LoginForm: UiLoginForm, StepForm: UiStepForm, Tag: UiTag, Kbd: UiKbd, StatsCard: UiStatsCard, KpiInline: UiKpiInline, MiniChart: UiMiniChart, Descriptions: UiDescriptions, LazyImage: UiLazyImage, SystemStatus: UiSystemStatus, TreeView: UiTreeView, CalendarView: UiCalendarView, SubNav: UiSubNav, ActionRail: UiActionRail, SplitLayout: UiSplitLayout, SettingsSection: UiSettingsSection, ScrollArea: UiScrollArea, DealCard: UiDealCard, PipelineBoard: UiPipelineBoard, ContactPanel: UiContactPanel, SalesFunnel: UiSalesFunnel, BookingCard: UiBookingCard, GuestCard: UiGuestCard, RoomGrid: UiRoomGrid, RoomCalendar: UiRoomCalendar, OccupancyChart: UiOccupancyChart, Chip: UiChip, IconTile: UiIconTile, StatusIcon: UiStatusIcon, UserCell: UiUserCell, StatRow: UiStatRow, CountryRow: UiCountryRow, StatLine: UiStatLine, StatStrip: UiStatStrip, VersusBar: UiVersusBar, SegBar: UiSegBar, HeroBanner: UiHeroBanner, TabTiles: UiTabTiles, PillTabs: UiPillTabs, PillSelect: UiPillSelect, ChartCard: UiChartCard, ActionCard: UiActionCard, Snackbar: UiSnackbar, TourPopover: UiTourPopover, ProfileHeader: UiProfileHeader, AuthSplit: UiAuthSplit, StatusPage: UiStatusPage, ValidatedField: UiValidatedField, Footer: UiFooter, DataGroup: UiDataGroup, OptionCard: UiOptionCard, OptionCards: UiOptionCards, RichEditor: UiRichEditor, RangeSlider: UiRangeSlider, RangeCalendar: UiRangeCalendar, StepDialog: UiStepDialog, FullscreenDialog: UiFullscreenDialog, PageDashboard: UiPageDashboard, PagePatterns: UiPagePatterns, PageCrm: UiPageCrm, PageBookings: UiPageBookings, PageCatalog: UiPageCatalog, PageDetail: UiPageDetail, PageSettings: UiPageSettings, PageUserList: UiPageUserList, PageProfile: UiPageProfile, PageForm: UiPageForm, PageOrder: UiPageOrder, PageAccount: UiPageAccount, PagePricing: UiPagePricing, PageFaq: UiPageFaq, PageAuth: UiPageAuth, PageStatus: UiPageStatus, TreeSelect: UiTreeSelect, ContextMenu: UiContextMenu, Sortable: UiSortable, Map: UiMap, ImageUpload: UiImageUpload, Screensaver: UiScreensaver, Tour: UiTour, Heading: UiHeading, Text: UiText, Box: UiBox, Center: UiCenter, HStack: UiHStack, VStack: UiVStack, Grid: UiGrid, Link: UiLink, Pressable: UiPressable, Icon: UiIcon, Portal: UiPortal, LiquidGlass: UiLiquidGlass, BottomSheet: UiBottomSheet, Actionsheet: UiActionsheet, ImageViewer: UiImageViewer, ChatAi: UiChatAi } = C;
32
+ export const { Button: UiButton, IconButton: UiIconButton, Badge: UiBadge, Avatar: UiAvatar, Switch: UiSwitch, Checkbox: UiCheckbox, SegmentedControl: UiSegmentedControl, Tabs: UiTabs, FilterChips: UiFilterChips, TextField: UiTextField, Select: UiSelect, CodeInput: UiCodeInput, Stepper: UiStepper, Card: UiCard, Modal: UiModal, Drawer: UiDrawer, Toast: UiToast, Breadcrumbs: UiBreadcrumbs, Pagination: UiPagination, Progress: UiProgress, Ring: UiRing, KpiTile: UiKpiTile, DataTable: UiDataTable, KanbanColumn: UiKanbanColumn, KanbanCard: UiKanbanCard, Timeline: UiTimeline, TopNav: UiTopNav, SideNav: UiSideNav, AppShell: UiAppShell, PageHeader: UiPageHeader, AuthLayout: UiAuthLayout, LeadCard: UiLeadCard, HotelCard: UiHotelCard, TourCard: UiTourCard, PrayerTimes: UiPrayerTimes, RamadanTracker: UiRamadanTracker, CallRow: UiCallRow, Alert: UiAlert, Banner: UiBanner, Accordion: UiAccordion, Skeleton: UiSkeleton, Spinner: UiSpinner, Divider: UiDivider, EmptyState: UiEmptyState, ErrorPage: UiErrorPage, DataState: UiDataState, LoadingOverlay: UiLoadingOverlay, ConfirmDialog: UiConfirmDialog, Popconfirm: UiPopconfirm, Carousel: UiCarousel, Gallery: UiGallery, Code: UiCode, PricingCard: UiPricingCard, IntegrationCard: UiIntegrationCard, IncomingCall: UiIncomingCall, CallCard: UiCallCard, Dropdown: UiDropdown, Menu: UiMenu, Tooltip: UiTooltip, HoverCard: UiHoverCard, Popover: UiPopover, CopyButton: UiCopyButton, CommandBar: UiCommandBar, CommandPalette: UiCommandPalette, NotificationBell: UiNotificationBell, ToggleGroup: UiToggleGroup, Fab: UiFab, Textarea: UiTextarea, NumberInput: UiNumberInput, PasswordInput: UiPasswordInput, SearchInput: UiSearchInput, RadioGroup: UiRadioGroup, Slider: UiSlider, Rating: UiRating, ChipInput: UiChipInput, InlineEdit: UiInlineEdit, ColorPicker: UiColorPicker, FileUpload: UiFileUpload, Combobox: UiCombobox, MultiSelect: UiMultiSelect, GroupedSelect: UiGroupedSelect, PhoneInput: UiPhoneInput, Calendar: UiCalendar, DatePicker: UiDatePicker, TimePicker: UiTimePicker, FormField: UiFormField, LoginForm: UiLoginForm, StepForm: UiStepForm, Tag: UiTag, Kbd: UiKbd, StatsCard: UiStatsCard, KpiInline: UiKpiInline, MiniChart: UiMiniChart, Descriptions: UiDescriptions, LazyImage: UiLazyImage, SystemStatus: UiSystemStatus, TreeView: UiTreeView, CalendarView: UiCalendarView, SubNav: UiSubNav, ActionRail: UiActionRail, SplitLayout: UiSplitLayout, SettingsSection: UiSettingsSection, ScrollArea: UiScrollArea, DealCard: UiDealCard, PipelineBoard: UiPipelineBoard, ContactPanel: UiContactPanel, SalesFunnel: UiSalesFunnel, BookingCard: UiBookingCard, GuestCard: UiGuestCard, RoomGrid: UiRoomGrid, RoomCalendar: UiRoomCalendar, OccupancyChart: UiOccupancyChart, Chip: UiChip, IconTile: UiIconTile, StatusIcon: UiStatusIcon, UserCell: UiUserCell, StatRow: UiStatRow, CountryRow: UiCountryRow, StatLine: UiStatLine, StatStrip: UiStatStrip, VersusBar: UiVersusBar, SegBar: UiSegBar, HeroBanner: UiHeroBanner, TabTiles: UiTabTiles, PillTabs: UiPillTabs, PillSelect: UiPillSelect, ChartCard: UiChartCard, ActionCard: UiActionCard, Snackbar: UiSnackbar, TourPopover: UiTourPopover, ProfileHeader: UiProfileHeader, AuthSplit: UiAuthSplit, AuthShowcase: UiAuthShowcase, SocialAuth: UiSocialAuth, StatusPage: UiStatusPage, ValidatedField: UiValidatedField, Footer: UiFooter, DataGroup: UiDataGroup, OptionCard: UiOptionCard, OptionCards: UiOptionCards, RichEditor: UiRichEditor, RangeSlider: UiRangeSlider, RangeCalendar: UiRangeCalendar, StepDialog: UiStepDialog, FullscreenDialog: UiFullscreenDialog, PageDashboard: UiPageDashboard, PagePatterns: UiPagePatterns, PageCrm: UiPageCrm, PageBookings: UiPageBookings, PageCatalog: UiPageCatalog, PageDetail: UiPageDetail, PageSettings: UiPageSettings, PageUserList: UiPageUserList, PageProfile: UiPageProfile, PageForm: UiPageForm, PageOrder: UiPageOrder, PageAccount: UiPageAccount, PagePricing: UiPagePricing, PageFaq: UiPageFaq, PageAuth: UiPageAuth, PageStatus: UiPageStatus, TreeSelect: UiTreeSelect, ContextMenu: UiContextMenu, Sortable: UiSortable, Map: UiMap, ImageUpload: UiImageUpload, Screensaver: UiScreensaver, Tour: UiTour, Heading: UiHeading, Text: UiText, Box: UiBox, Center: UiCenter, HStack: UiHStack, VStack: UiVStack, Grid: UiGrid, Link: UiLink, Pressable: UiPressable, Icon: UiIcon, Portal: UiPortal, LiquidGlass: UiLiquidGlass, BottomSheet: UiBottomSheet, Actionsheet: UiActionsheet, ImageViewer: UiImageViewer, ChatAi: UiChatAi } = C;
33
33
  export const install = app => { for (const [n, c] of Object.entries(C)) app.component('Ui' + n, c); };
34
34
  export { fmt, setIconBase, rules } from '../core/index.js';
35
35
  export { useUiTheme } from '../theme.js';
package/types/react.d.ts CHANGED
@@ -236,6 +236,44 @@ export interface UiAuthLayoutProps {
236
236
  }
237
237
  export const UiAuthLayout: ComponentType<UiAuthLayoutProps>;
238
238
 
239
+ /**
240
+ * Sign-in layout with a media panel: dark photo card with floating avatars, light gradient card with a social-proof pill, or a tinted panel with a glass caption. The default slot is the form.
241
+ * Note: logo, footer, image, people are props in both Vue and React. The media panel is hidden below the form on narrow screens (stacks under it).
242
+ */
243
+ export interface UiAuthShowcaseProps {
244
+ /**
245
+ * 'photo': one framed card, media on the left. 'light': gradient form card, proof pill below, glass caption on the media. 'tint': like light, with a coloured panel, a heading on the media and cyan accent.
246
+ * @default 'photo'
247
+ */
248
+ variant?: 'photo' | 'light' | 'tint';
249
+ /** Form heading. */
250
+ title: string;
251
+ /** Text under the heading. */
252
+ subtitle?: string;
253
+ /** Logo node above the heading. */
254
+ logo?: ReactNode;
255
+ /** Content under the fields, for example "No account? Sign up". */
256
+ footer?: ReactNode;
257
+ /** URL of the media image. Without it the panel shows an accent gradient. */
258
+ image?: string;
259
+ /** Large text on the media panel (photo and tint). */
260
+ headline?: string;
261
+ /** Small text on the media panel (photo) or inside the glass bar (light, tint). */
262
+ caption?: string;
263
+ /** Line at the bottom of the media panel; photo only. */
264
+ quote?: string;
265
+ /** Pill labels inside the glass bar; light and tint. */
266
+ chips?: string[];
267
+ /** Avatars: floating on the photo, stacked in the proof pill. */
268
+ people?: { name: string; src?: string }[];
269
+ /** Social-proof title, e.g. "Join 20k+ users"; light and tint. Shows the pill under the form. */
270
+ proof?: string;
271
+ /** Second line of the proof pill. */
272
+ proofHint?: string;
273
+ children?: ReactNode;
274
+ }
275
+ export const UiAuthShowcase: ComponentType<UiAuthShowcaseProps>;
276
+
239
277
  /**
240
278
  * Two-column sign-in layout with a brand panel on the left and the form on the right.
241
279
  * Note: logo and aside are props in both Vue and React (pass a VNode / element).
@@ -3058,11 +3096,51 @@ export const UiPageAccount: ComponentType<UiPageAccountProps>;
3058
3096
  */
3059
3097
  export interface UiPageAuthProps {
3060
3098
  /**
3061
- * 'split' uses AuthSplit with a side panel; anything else renders a centered AuthLayout card over the background.
3099
+ * 'split' uses AuthSplit with a side panel. 'photo', 'light' and 'tint' use AuthShowcase with a media panel. Anything else renders a centered AuthLayout card over the background.
3062
3100
  * @default 'card'
3063
3101
  * @remarks Forwarded to the inner element.
3064
3102
  */
3065
- layout?: 'card' | 'split';
3103
+ layout?: 'card' | 'split' | 'photo' | 'light' | 'tint';
3104
+ /**
3105
+ * Media image URL; photo, light and tint layouts.
3106
+ * @remarks Forwarded to the inner element.
3107
+ */
3108
+ image?: string;
3109
+ /**
3110
+ * Large text on the media panel; photo and tint.
3111
+ * @remarks Forwarded to the inner element.
3112
+ */
3113
+ headline?: string;
3114
+ /**
3115
+ * Small text on the media panel or glass bar.
3116
+ * @remarks Forwarded to the inner element.
3117
+ */
3118
+ caption?: string;
3119
+ /**
3120
+ * Bottom line of the media panel; photo only.
3121
+ * @remarks Forwarded to the inner element.
3122
+ */
3123
+ quote?: string;
3124
+ /**
3125
+ * Pill labels in the glass bar; light and tint.
3126
+ * @remarks Forwarded to the inner element.
3127
+ */
3128
+ chips?: string[];
3129
+ /**
3130
+ * Avatars on the media panel or in the proof pill.
3131
+ * @remarks Forwarded to the inner element.
3132
+ */
3133
+ people?: { name: string; src?: string }[];
3134
+ /**
3135
+ * Social-proof title under the form; light and tint.
3136
+ * @remarks Forwarded to the inner element.
3137
+ */
3138
+ proof?: string;
3139
+ /**
3140
+ * Second line of the proof pill.
3141
+ * @remarks Forwarded to the inner element.
3142
+ */
3143
+ proofHint?: string;
3066
3144
  /**
3067
3145
  * Form heading.
3068
3146
  * @remarks Forwarded to the inner element.
@@ -5047,6 +5125,39 @@ export interface UiSnackbarProps {
5047
5125
  }
5048
5126
  export const UiSnackbar: ComponentType<UiSnackbarProps>;
5049
5127
 
5128
+ /**
5129
+ * Continue with Google and Apple buttons with an "or" divider. Place it under the sign-in form.
5130
+ * Note: The kit only renders the buttons. The OAuth flow itself is yours.
5131
+ */
5132
+ export interface UiSocialAuthProps {
5133
+ /**
5134
+ * Which buttons to show, in order.
5135
+ * @default ['google', 'apple']
5136
+ */
5137
+ providers?: ('google' | 'apple')[];
5138
+ /**
5139
+ * 'stack': full-width buttons with a label, one per row. 'icons': a row of square icon buttons.
5140
+ * @default 'stack'
5141
+ */
5142
+ variant?: 'stack' | 'icons';
5143
+ /**
5144
+ * Show the "or" line above the buttons.
5145
+ * @default 'true'
5146
+ */
5147
+ divider?: boolean;
5148
+ /** Provider names inserted into "Continue with {provider}". */
5149
+ labels?: { google?: string; apple?: string };
5150
+ /** Called with the clicked provider. Start your OAuth flow here. */
5151
+ onSelect?: (provider: 'google' | 'apple') => void;
5152
+ /**
5153
+ * Scales padding, text and avatars.
5154
+ * @default 'md'
5155
+ */
5156
+ size?: Size;
5157
+ children?: ReactNode;
5158
+ }
5159
+ export const UiSocialAuth: ComponentType<UiSocialAuthProps>;
5160
+
5050
5161
  /**
5051
5162
  * Drag-and-drop reorderable list with grip handles and up/down buttons.
5052
5163
  * Note: Controlled: the parent must store the array from change and pass it back as items.
@@ -13,6 +13,7 @@ declare module 'vue' {
13
13
  UiAlert: typeof Ui.UiAlert;
14
14
  UiAppShell: typeof Ui.UiAppShell;
15
15
  UiAuthLayout: typeof Ui.UiAuthLayout;
16
+ UiAuthShowcase: typeof Ui.UiAuthShowcase;
16
17
  UiAuthSplit: typeof Ui.UiAuthSplit;
17
18
  UiAvatar: typeof Ui.UiAvatar;
18
19
  UiBadge: typeof Ui.UiBadge;
@@ -152,6 +153,7 @@ declare module 'vue' {
152
153
  UiSkeleton: typeof Ui.UiSkeleton;
153
154
  UiSlider: typeof Ui.UiSlider;
154
155
  UiSnackbar: typeof Ui.UiSnackbar;
156
+ UiSocialAuth: typeof Ui.UiSocialAuth;
155
157
  UiSortable: typeof Ui.UiSortable;
156
158
  UiSpinner: typeof Ui.UiSpinner;
157
159
  UiSplitLayout: typeof Ui.UiSplitLayout;
package/types/vue.d.ts CHANGED
@@ -244,6 +244,47 @@ export interface UiAuthLayoutSlots {
244
244
  }
245
245
  export const UiAuthLayout: UiVueComponent<UiAuthLayoutProps, UiAuthLayoutSlots>;
246
246
 
247
+ /**
248
+ * Sign-in layout with a media panel: dark photo card with floating avatars, light gradient card with a social-proof pill, or a tinted panel with a glass caption. The default slot is the form.
249
+ * Note: logo, footer, image, people are props in both Vue and React. The media panel is hidden below the form on narrow screens (stacks under it).
250
+ */
251
+ export interface UiAuthShowcaseProps {
252
+ /**
253
+ * 'photo': one framed card, media on the left. 'light': gradient form card, proof pill below, glass caption on the media. 'tint': like light, with a coloured panel, a heading on the media and cyan accent.
254
+ * @default 'photo'
255
+ */
256
+ variant?: 'photo' | 'light' | 'tint';
257
+ /** Form heading. */
258
+ title: string;
259
+ /** Text under the heading. */
260
+ subtitle?: string;
261
+ /** Logo node above the heading. */
262
+ logo?: VNode;
263
+ /** Content under the fields, for example "No account? Sign up". */
264
+ footer?: VNode;
265
+ /** URL of the media image. Without it the panel shows an accent gradient. */
266
+ image?: string;
267
+ /** Large text on the media panel (photo and tint). */
268
+ headline?: string;
269
+ /** Small text on the media panel (photo) or inside the glass bar (light, tint). */
270
+ caption?: string;
271
+ /** Line at the bottom of the media panel; photo only. */
272
+ quote?: string;
273
+ /** Pill labels inside the glass bar; light and tint. */
274
+ chips?: string[];
275
+ /** Avatars: floating on the photo, stacked in the proof pill. */
276
+ people?: { name: string; src?: string }[];
277
+ /** Social-proof title, e.g. "Join 20k+ users"; light and tint. Shows the pill under the form. */
278
+ proof?: string;
279
+ /** Second line of the proof pill. */
280
+ proofHint?: string;
281
+ }
282
+ export interface UiAuthShowcaseSlots {
283
+ /** Form fields and buttons. React passes them as children. */
284
+ default?: () => any;
285
+ }
286
+ export const UiAuthShowcase: UiVueComponent<UiAuthShowcaseProps, UiAuthShowcaseSlots>;
287
+
247
288
  /**
248
289
  * Two-column sign-in layout with a brand panel on the left and the form on the right.
249
290
  * Note: logo and aside are props in both Vue and React (pass a VNode / element).
@@ -3164,11 +3205,51 @@ export const UiPageAccount: UiVueComponent<UiPageAccountProps, UiPageAccountSlot
3164
3205
  */
3165
3206
  export interface UiPageAuthProps {
3166
3207
  /**
3167
- * 'split' uses AuthSplit with a side panel; anything else renders a centered AuthLayout card over the background.
3208
+ * 'split' uses AuthSplit with a side panel. 'photo', 'light' and 'tint' use AuthShowcase with a media panel. Anything else renders a centered AuthLayout card over the background.
3168
3209
  * @default 'card'
3169
3210
  * @remarks Forwarded to the inner element.
3170
3211
  */
3171
- layout?: 'card' | 'split';
3212
+ layout?: 'card' | 'split' | 'photo' | 'light' | 'tint';
3213
+ /**
3214
+ * Media image URL; photo, light and tint layouts.
3215
+ * @remarks Forwarded to the inner element.
3216
+ */
3217
+ image?: string;
3218
+ /**
3219
+ * Large text on the media panel; photo and tint.
3220
+ * @remarks Forwarded to the inner element.
3221
+ */
3222
+ headline?: string;
3223
+ /**
3224
+ * Small text on the media panel or glass bar.
3225
+ * @remarks Forwarded to the inner element.
3226
+ */
3227
+ caption?: string;
3228
+ /**
3229
+ * Bottom line of the media panel; photo only.
3230
+ * @remarks Forwarded to the inner element.
3231
+ */
3232
+ quote?: string;
3233
+ /**
3234
+ * Pill labels in the glass bar; light and tint.
3235
+ * @remarks Forwarded to the inner element.
3236
+ */
3237
+ chips?: string[];
3238
+ /**
3239
+ * Avatars on the media panel or in the proof pill.
3240
+ * @remarks Forwarded to the inner element.
3241
+ */
3242
+ people?: { name: string; src?: string }[];
3243
+ /**
3244
+ * Social-proof title under the form; light and tint.
3245
+ * @remarks Forwarded to the inner element.
3246
+ */
3247
+ proof?: string;
3248
+ /**
3249
+ * Second line of the proof pill.
3250
+ * @remarks Forwarded to the inner element.
3251
+ */
3252
+ proofHint?: string;
3172
3253
  /**
3173
3254
  * Form heading.
3174
3255
  * @remarks Forwarded to the inner element.
@@ -4915,6 +4996,39 @@ export interface UiSnackbarSlots {
4915
4996
  }
4916
4997
  export const UiSnackbar: UiVueComponent<UiSnackbarProps, UiSnackbarSlots>;
4917
4998
 
4999
+ /**
5000
+ * Continue with Google and Apple buttons with an "or" divider. Place it under the sign-in form.
5001
+ * Note: The kit only renders the buttons. The OAuth flow itself is yours.
5002
+ */
5003
+ export interface UiSocialAuthProps {
5004
+ /**
5005
+ * Which buttons to show, in order.
5006
+ * @default ['google', 'apple']
5007
+ */
5008
+ providers?: ('google' | 'apple')[];
5009
+ /**
5010
+ * 'stack': full-width buttons with a label, one per row. 'icons': a row of square icon buttons.
5011
+ * @default 'stack'
5012
+ */
5013
+ variant?: 'stack' | 'icons';
5014
+ /**
5015
+ * Show the "or" line above the buttons.
5016
+ * @default 'true'
5017
+ */
5018
+ divider?: boolean;
5019
+ /** Provider names inserted into "Continue with {provider}". */
5020
+ labels?: { google?: string; apple?: string };
5021
+ /** Called with the clicked provider. Start your OAuth flow here. */
5022
+ onSelect?: (provider: 'google' | 'apple') => void;
5023
+ /**
5024
+ * Scales padding, text and avatars.
5025
+ * @default 'md'
5026
+ */
5027
+ size?: Size;
5028
+ }
5029
+ export interface UiSocialAuthSlots {}
5030
+ export const UiSocialAuth: UiVueComponent<UiSocialAuthProps, UiSocialAuthSlots>;
5031
+
4918
5032
  /**
4919
5033
  * Drag-and-drop reorderable list with grip handles and up/down buttons.
4920
5034
  * Note: Controlled: the parent must store the array from change and pass it back as items.