@nuxtseo/design-system 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/README.md +181 -2
  2. package/app/app.config.ts +592 -0
  3. package/app/component-attributes.d.ts +8 -0
  4. package/app/components/README.md +38 -0
  5. package/app/components/brand/UiBrandMark.vue +63 -0
  6. package/app/components/brand/UiModuleIcon.vue +45 -0
  7. package/app/components/brand/UiProLettering.vue +26 -0
  8. package/app/components/brand/UiRangeTerrain.vue +311 -0
  9. package/app/components/brand/range-glyphs.ts +18 -0
  10. package/app/components/container/BrowserTab.vue +145 -0
  11. package/app/components/container/IntegrationRow.vue +120 -0
  12. package/app/components/container/KnowDoRails.vue +40 -0
  13. package/app/components/container/RailPanel.vue +93 -0
  14. package/app/components/container/UiAlert.vue +120 -0
  15. package/app/components/container/UiAppShell.vue +165 -0
  16. package/app/components/container/UiCard.vue +204 -0
  17. package/app/components/container/UiDisclosure.vue +43 -0
  18. package/app/components/container/UiEmptyState.vue +102 -0
  19. package/app/components/container/UiErrorState.vue +61 -0
  20. package/app/components/container/UiLoadingState.vue +20 -0
  21. package/app/components/container/UiPageHeader.vue +95 -0
  22. package/app/components/container/UiPopover.vue +102 -0
  23. package/app/components/container/UiSampleDataOverlay.vue +47 -0
  24. package/app/components/container/UiSectionHeader.vue +83 -0
  25. package/app/components/container/UiSummaryStrip.vue +40 -0
  26. package/app/components/container/UiToast.vue +230 -0
  27. package/app/components/container/UiToaster.vue +271 -0
  28. package/app/components/container/UiWidgetState.vue +143 -0
  29. package/app/components/container/ui-nav-drawer.ts +48 -0
  30. package/app/components/data/ActionRail.vue +152 -0
  31. package/app/components/data/FacetStrip.vue +93 -0
  32. package/app/components/data/UiBarCell.vue +37 -0
  33. package/app/components/data/UiBarChart.vue +281 -0
  34. package/app/components/data/UiChartAnnotationControls.vue +74 -0
  35. package/app/components/data/UiChartAnnotations.vue +163 -0
  36. package/app/components/data/UiChartBrushOverlay.vue +96 -0
  37. package/app/components/data/UiChartDataReveal.vue +81 -0
  38. package/app/components/data/UiChartFrame.vue +123 -0
  39. package/app/components/data/UiChartTooltip.vue +81 -0
  40. package/app/components/data/UiCoverageNote.vue +90 -0
  41. package/app/components/data/UiCwvSparkline.vue +188 -0
  42. package/app/components/data/UiDataList.vue +276 -0
  43. package/app/components/data/UiDataTableSection.vue +233 -0
  44. package/app/components/data/UiDistributionBars.vue +164 -0
  45. package/app/components/data/UiDonutChart.vue +132 -0
  46. package/app/components/data/UiDoorwayColumn.vue +108 -0
  47. package/app/components/data/UiDoorwayGrid.vue +58 -0
  48. package/app/components/data/UiDoorwayRow.vue +83 -0
  49. package/app/components/data/UiEntitySummary.vue +261 -0
  50. package/app/components/data/UiFactsGrid.vue +107 -0
  51. package/app/components/data/UiImpactMeter.vue +33 -0
  52. package/app/components/data/UiIssueRow.vue +149 -0
  53. package/app/components/data/UiLineChart.vue +431 -0
  54. package/app/components/data/UiMetricBarPopover.vue +121 -0
  55. package/app/components/data/UiMetricLabel.vue +30 -0
  56. package/app/components/data/UiMetricStat.vue +73 -0
  57. package/app/components/data/UiMetricToggle.vue +156 -0
  58. package/app/components/data/UiMetricsRow.vue +64 -0
  59. package/app/components/data/UiPositionMetric.vue +53 -0
  60. package/app/components/data/UiRelatedFixes.vue +113 -0
  61. package/app/components/data/UiRichText.vue +325 -0
  62. package/app/components/data/UiScatterPlot.vue +144 -0
  63. package/app/components/data/UiSeverityMarker.vue +30 -0
  64. package/app/components/data/UiSparkColumns.vue +160 -0
  65. package/app/components/data/UiSparkline.vue +681 -0
  66. package/app/components/data/UiStat.vue +445 -0
  67. package/app/components/data/UiStats.vue +76 -0
  68. package/app/components/data/UiTable.vue +517 -0
  69. package/app/components/data/UiTableExport.vue +40 -0
  70. package/app/components/data/UiTableFrame.vue +149 -0
  71. package/app/components/data/UiTablePagination.vue +37 -0
  72. package/app/components/data/UiTableShell.vue +48 -0
  73. package/app/components/data/UiTableTd.vue +39 -0
  74. package/app/components/data/UiTableTh.vue +61 -0
  75. package/app/components/data/UiTopEntityStackChart.vue +393 -0
  76. package/app/components/data/cells/UiTableDash.vue +3 -0
  77. package/app/components/data/cells/UiTableHeaderCell.vue +72 -0
  78. package/app/components/data/cells/UiTableMetricCell.vue +72 -0
  79. package/app/components/data/cells/UiTablePathCell.vue +76 -0
  80. package/app/components/data/cells/UiTableScoreTile.vue +49 -0
  81. package/app/components/data/cells/UiTableTrendCell.vue +41 -0
  82. package/app/components/element/UiAtmosphere.vue +64 -0
  83. package/app/components/element/UiAuthHeading.vue +24 -0
  84. package/app/components/element/UiAuthProviders.vue +46 -0
  85. package/app/components/element/UiBrandIcon.vue +39 -0
  86. package/app/components/element/UiButton.vue +549 -0
  87. package/app/components/element/UiCelebration.vue +65 -0
  88. package/app/components/element/UiChannelChips.vue +62 -0
  89. package/app/components/element/UiChatQuickLink.vue +319 -0
  90. package/app/components/element/UiChip.vue +101 -0
  91. package/app/components/element/UiConfirmDialog.vue +84 -0
  92. package/app/components/element/UiCopyButton.vue +90 -0
  93. package/app/components/element/UiDitherAvatar.vue +177 -0
  94. package/app/components/element/UiDotLabel.vue +19 -0
  95. package/app/components/element/UiEjectMenu.vue +181 -0
  96. package/app/components/element/UiEjectOverlay.vue +29 -0
  97. package/app/components/element/UiFavicon.vue +235 -0
  98. package/app/components/element/UiFaviconStack.vue +84 -0
  99. package/app/components/element/UiFilterMenu.vue +324 -0
  100. package/app/components/element/UiFocusedSurface.vue +129 -0
  101. package/app/components/element/UiHealthDot.vue +32 -0
  102. package/app/components/element/UiHelpLabel.vue +28 -0
  103. package/app/components/element/UiIcon.vue +22 -0
  104. package/app/components/element/UiInput.vue +35 -0
  105. package/app/components/element/UiLogoPlausible.vue +12 -0
  106. package/app/components/element/UiLogoPro.vue +25 -0
  107. package/app/components/element/UiLogoUmami.vue +12 -0
  108. package/app/components/element/UiNavIcon.vue +107 -0
  109. package/app/components/element/UiNavList.vue +156 -0
  110. package/app/components/element/UiOnboardingAnswers.vue +127 -0
  111. package/app/components/element/UiOverflowCount.vue +31 -0
  112. package/app/components/element/UiProBadge.vue +19 -0
  113. package/app/components/element/UiProgressCircle.vue +79 -0
  114. package/app/components/element/UiProgressPercent.vue +35 -0
  115. package/app/components/element/UiRelativeTime.vue +58 -0
  116. package/app/components/element/UiScrollFade.vue +16 -0
  117. package/app/components/element/UiSelect.vue +68 -0
  118. package/app/components/element/UiSeverityDot.vue +27 -0
  119. package/app/components/element/UiSiteFavicon.vue +24 -0
  120. package/app/components/element/UiSkeleton.vue +148 -0
  121. package/app/components/element/UiSlotText.vue +65 -0
  122. package/app/components/element/UiSourceLogos.vue +113 -0
  123. package/app/components/element/UiSourceRail.vue +71 -0
  124. package/app/components/element/UiStaticTerminal.vue +107 -0
  125. package/app/components/element/UiStatusBadge.vue +62 -0
  126. package/app/components/element/UiSyncDot.vue +46 -0
  127. package/app/components/element/UiTabs.vue +245 -0
  128. package/app/components/element/UiTogglePill.vue +86 -0
  129. package/app/components/element/UiTooltip.vue +390 -0
  130. package/app/components/element/UiTooltipGrid.vue +457 -0
  131. package/app/components/element/UiTrend.vue +184 -0
  132. package/app/components/element/UiTrialBenefits.vue +71 -0
  133. package/app/components/element/UiUrlLabel.vue +75 -0
  134. package/app/components/element/UiValenceDot.vue +37 -0
  135. package/app/components/element/UiWizardModal.vue +284 -0
  136. package/app/components/element/UiWizardNav.vue +59 -0
  137. package/app/components/element/UiWizardProgress.vue +78 -0
  138. package/app/components/element/UiWizardShell.vue +43 -0
  139. package/app/components/element/eject-menu.ts +359 -0
  140. package/app/components/element/eject-tracker.ts +34 -0
  141. package/app/components/element/onboarding-answers.ts +93 -0
  142. package/app/components/element/onboarding-intents.ts +19 -0
  143. package/app/composables/dataVizColors.ts +350 -0
  144. package/app/composables/formatting.ts +252 -0
  145. package/app/composables/semanticColors.ts +80 -0
  146. package/app/composables/useActivityOverlay.ts +55 -0
  147. package/app/composables/useAppShellNav.ts +21 -0
  148. package/app/composables/useChartBrush.ts +221 -0
  149. package/app/composables/useChartHover.ts +153 -0
  150. package/app/composables/useChartTickPlan.ts +186 -0
  151. package/app/composables/useCloseOnNavigation.ts +7 -0
  152. package/app/composables/useConfirm.ts +38 -0
  153. package/app/composables/useEdgeFade.ts +37 -0
  154. package/app/composables/useEject.ts +69 -0
  155. package/app/composables/useEntranceMotion.ts +22 -0
  156. package/app/composables/useFaviconBacking.ts +164 -0
  157. package/app/composables/useGridCols.ts +39 -0
  158. package/app/composables/useIssueOverlay.ts +43 -0
  159. package/app/composables/useOnboardingService.ts +106 -0
  160. package/app/composables/useOpenedOnce.ts +19 -0
  161. package/app/composables/useScrollSpy.ts +178 -0
  162. package/app/composables/useSquircleFallback.ts +21 -0
  163. package/app/composables/useWizardRoute.ts +123 -0
  164. package/app/plugins/favicon.ts +15 -0
  165. package/app/plugins/lazy-motion.ts +22 -0
  166. package/app/plugins/selection-highlight.client.ts +176 -0
  167. package/app/shared/cli.ts +10 -0
  168. package/app/shared/error-state.ts +150 -0
  169. package/app/shared/icons.ts +1 -0
  170. package/app/shared/motion.ts +1 -0
  171. package/app/shared/nav.ts +38 -0
  172. package/app/shared/navigation.ts +5 -0
  173. package/app/shared/static-terminal.ts +14 -0
  174. package/app/shared/table-export.ts +36 -0
  175. package/app/shared/table.ts +105 -0
  176. package/app/shared/ui-icons.ts +1 -0
  177. package/app/shared/view-state.ts +85 -0
  178. package/app/shared/widget-failure.ts +126 -0
  179. package/app/utils/app-favicon.ts +64 -0
  180. package/app/utils/chartAnnotations.ts +326 -0
  181. package/app/utils/countries.ts +337 -0
  182. package/app/utils/export-download.ts +100 -0
  183. package/app/utils/favicon-fallback.ts +12 -0
  184. package/app/utils/icon-color.ts +105 -0
  185. package/app/utils/number.ts +74 -0
  186. package/app/utils/render-link.ts +14 -0
  187. package/app/utils/slot-text.ts +18 -0
  188. package/app/utils/squircle.ts +154 -0
  189. package/app/utils/topEntityStack.ts +197 -0
  190. package/app/utils/vue.ts +57 -0
  191. package/build/design-system.ts +69 -0
  192. package/build/external-links.ts +38 -0
  193. package/build/fonts.ts +39 -0
  194. package/build/icon-server-bundle.ts +276 -0
  195. package/build/runtime/icon-server-bundle.ts +29 -0
  196. package/css/global.css +1896 -0
  197. package/nuxt.config.ts +143 -0
  198. package/package.json +93 -3
  199. package/public/fonts/README.md +33 -0
  200. package/public/fonts/fira-code-OFL.txt +93 -0
  201. package/public/fonts/fira-code-var-latin.woff2 +0 -0
  202. package/public/fonts/hubot-sans-OFL.txt +93 -0
  203. package/public/fonts/hubot-sans-var-latin.woff2 +0 -0
  204. package/server/api/favicon.get.ts +122 -0
  205. package/server/utils/favicon-source.ts +51 -0
  206. package/shared/contracts/feedback.ts +42 -0
  207. package/shared/contracts/index.ts +1 -0
  208. package/shared/delivery-channels.ts +53 -0
  209. package/shared/icons/index.ts +143 -0
  210. package/shared/icons/registry.ts +347 -0
  211. package/shared/icons/ui-slots.ts +58 -0
  212. package/shared/markdown.ts +44 -0
  213. package/shared/motion.ts +120 -0
  214. package/shared/rangi.ts +60 -0
  215. package/shared/rich-text.ts +34 -0
  216. package/shared/ui-icons.ts +6 -0
  217. package/shared/utils/urls.ts +64 -0
  218. package/types/index.ts +1 -0
  219. package/types/ui.ts +339 -0
@@ -0,0 +1,41 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { UiTableDash, UiTrend } from '#components'
4
+ import { calcTrendPercent } from '../../../composables/formatting'
5
+
6
+ const {
7
+ current,
8
+ previous,
9
+ inverted = false,
10
+ size = '2xs',
11
+ align = 'right',
12
+ } = defineProps<{
13
+ current: number | null | undefined
14
+ previous: number | null | undefined
15
+ /** When true, a decrease is treated as positive (e.g. position ranking). */
16
+ inverted?: boolean
17
+ size?: '2xs' | 'xs' | 'sm'
18
+ align?: 'left' | 'center' | 'right'
19
+ }>()
20
+
21
+ // Raw change only. UiTrend owns the `inverted` handling, so flipping the sign
22
+ // here too would point the number against its own arrow and colour. A rise
23
+ // from 0 has no percent: it is new, which a dash ("no data") hid.
24
+ const change = computed<{ _tag: 'Missing' } | { _tag: 'New' } | { _tag: 'Percent', value: number }>(() => {
25
+ if (current == null || previous == null)
26
+ return { _tag: 'Missing' }
27
+ if (previous === 0)
28
+ return current > 0 ? { _tag: 'New' } : { _tag: 'Missing' }
29
+ return { _tag: 'Percent', value: calcTrendPercent(current, previous) }
30
+ })
31
+
32
+ const alignClass = computed(() => align === 'right' ? 'justify-end' : align === 'center' ? 'justify-center' : 'justify-start')
33
+ </script>
34
+
35
+ <template>
36
+ <div class="flex items-center" :class="alignClass">
37
+ <UiTableDash v-if="change._tag === 'Missing'" />
38
+ <UiTrend v-else-if="change._tag === 'New'" is-new :size="size" />
39
+ <UiTrend v-else :value="change.value" format="percent" :inverted="inverted" :size="size" />
40
+ </div>
41
+ </template>
@@ -0,0 +1,64 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ // The greydient application primitive. Turns intent into a correctly-rendered
4
+ // atmospheric layer — palette × geometry × intensity, plus grain — with the
5
+ // mask/opacity/mode-handling all centralized in global.css (`.ui-atmosphere`).
6
+ // Consumers place it as the first child of a `relative` container (content then
7
+ // sits above it); for `sky` it spans the top of the nearest positioned ancestor.
8
+ //
9
+ // This is the ONE place greydient atmosphere is produced. Never on data surfaces
10
+ // (charts/tables/metrics); contained identity chrome follows DESIGN's explicit
11
+ // micro-surface exception — "atmosphere at the edges, silence at the center".
12
+ type Palette = 'ink' | 'twilight' | 'dawn' | 'dusk' | 'dusk-vivid' | 'mist' | 'ash'
13
+ type Geometry = 'sky' | 'bloom' | 'wash' | 'veil'
14
+ type Intensity = 'subtle' | 'ambient' | 'present' | 'vivid'
15
+ type Preset = 'front-door' | 'error' | 'ai' | 'preview' | 'share'
16
+
17
+ const {
18
+ palette: paletteProp,
19
+ geometry: geometryProp,
20
+ intensity: intensityProp,
21
+ grain = true,
22
+ preset,
23
+ } = defineProps<{
24
+ /** Which greydient — the day-cycle of visibility states. */
25
+ palette?: Palette
26
+ /** How it's shaped. sky = full-bleed top; bloom = soft glow; wash = framed directional; veil = preview scrim. */
27
+ geometry?: Geometry
28
+ /** How present, per mode. subtle behind text; vivid is reserved for tiny identity surfaces. */
29
+ intensity?: Intensity
30
+ /** Fine grain for tooth. Default on. */
31
+ grain?: boolean
32
+ /** Semantic shortcut — resolves palette+geometry+intensity from one intent. Explicit props win. */
33
+ preset?: Preset
34
+ }>()
35
+
36
+ // The semantic map — the design decision ("front door = dusk bloom") lives HERE,
37
+ // once, instead of scattered across surfaces. Explicit props still override.
38
+ // Canonical palette roles (DESIGN / brand-kit): ink = OG/share ground · twilight
39
+ // = auth · dawn = marketing warmth · dusk = hero (spends color) · mist = behind
40
+ // text · ash = neutral. Presets encode those roles once.
41
+ const PRESETS: Record<Preset, { palette: Palette, geometry: Geometry, intensity: Intensity }> = {
42
+ 'front-door': { palette: 'twilight', geometry: 'bloom', intensity: 'present' },
43
+ 'error': { palette: 'twilight', geometry: 'sky', intensity: 'subtle' },
44
+ 'ai': { palette: 'dusk', geometry: 'bloom', intensity: 'ambient' },
45
+ 'preview': { palette: 'mist', geometry: 'veil', intensity: 'subtle' },
46
+ 'share': { palette: 'ink', geometry: 'wash', intensity: 'present' },
47
+ }
48
+
49
+ const base = computed(() => (preset ? PRESETS[preset] : { palette: 'ash' as Palette, geometry: 'wash' as Geometry, intensity: 'ambient' as Intensity }))
50
+ const palette = computed(() => paletteProp ?? base.value.palette)
51
+ const geometry = computed(() => geometryProp ?? base.value.geometry)
52
+ const intensity = computed(() => intensityProp ?? base.value.intensity)
53
+ </script>
54
+
55
+ <template>
56
+ <div
57
+ aria-hidden="true"
58
+ class="ui-atmosphere"
59
+ :data-geometry="geometry"
60
+ :data-intensity="intensity"
61
+ :data-grain="grain ? '' : undefined"
62
+ :style="{ '--_atmo-greydient': `var(--greydient-${palette})` }"
63
+ />
64
+ </template>
@@ -0,0 +1,24 @@
1
+ <script setup lang="ts">
2
+ // Presentational heading for the auth/onboarding panels (login, onboarding,
3
+ // brand-kit showcase). Title + optional description, in the canonical auth
4
+ // type scale. Pure presentation — no runtime services.
5
+ const { title, description, size = 'lg' } = defineProps<{
6
+ title: string
7
+ description?: string
8
+ size?: 'md' | 'lg'
9
+ }>()
10
+ </script>
11
+
12
+ <template>
13
+ <div class="mb-6">
14
+ <h1
15
+ class="font-semibold tracking-tight text-highlighted leading-[1.1] mb-3"
16
+ :class="size === 'lg' ? 'text-3xl' : 'text-2xl'"
17
+ >
18
+ {{ title }}
19
+ </h1>
20
+ <p v-if="description" class="text-muted text-sm leading-relaxed">
21
+ {{ description }}
22
+ </p>
23
+ </div>
24
+ </template>
@@ -0,0 +1,46 @@
1
+ <script setup lang="ts">
2
+ import { UiButton } from '#components'
3
+ // Provider sign-in/sign-up button list shared by the login + onboarding auth
4
+ // panels (and the brand-kit showcase). The first provider renders as the CTA,
5
+ // the rest as secondary; a "Last used" label marks the promoted provider.
6
+ //
7
+ // Pure presentation: labels, hrefs and ordering are computed by the consumer
8
+ // (each page builds the source param + copy). This component only renders and
9
+ // emits `select` so the consumer can persist the last-used provider.
10
+ export interface UiAuthProvider {
11
+ id: string
12
+ label: string
13
+ icon: string
14
+ href?: string
15
+ external?: boolean
16
+ lastUsed?: boolean
17
+ testId?: string
18
+ }
19
+
20
+ const { providers } = defineProps<{ providers: UiAuthProvider[] }>()
21
+ const emit = defineEmits<{ select: [id: string] }>()
22
+ </script>
23
+
24
+ <template>
25
+ <div class="flex flex-col gap-3">
26
+ <div v-for="(p, idx) in providers" :key="p.id" class="relative">
27
+ <UiButton
28
+ :data-testid="p.testId"
29
+ :to="p.href"
30
+ :external="p.external"
31
+ :purpose="idx === 0 ? 'cta' : 'secondary'"
32
+ size="lg"
33
+ :icon="p.icon"
34
+ block
35
+ @click="emit('select', p.id)"
36
+ >
37
+ {{ p.label }}
38
+ <template v-if="p.lastUsed && providers.length > 1" #trailing>
39
+ <span class="absolute right-4 text-[11px] font-medium tracking-wide opacity-60">
40
+ Last used
41
+ </span>
42
+ </template>
43
+ </UiButton>
44
+ </div>
45
+ </div>
46
+ </template>
@@ -0,0 +1,39 @@
1
+ <script setup lang="ts">
2
+ import { drawingToSvg, faviconDrawing, markDrawing } from 'nuxtseo-shared/brand'
3
+ import { computed, useId } from 'vue'
4
+
5
+ const { size = 'md', variant = 'tile' } = defineProps<{
6
+ size?: 'md' | 'lg'
7
+ variant?: 'tile' | 'mark'
8
+ }>()
9
+
10
+ const sizeClasses = {
11
+ md: 'size-7',
12
+ lg: 'size-8',
13
+ } as const
14
+
15
+ const pixelSizes = {
16
+ md: 28,
17
+ lg: 32,
18
+ } as const
19
+
20
+ // Pro surfaces only. The tile is the Pro favicon: the summit on the plate with
21
+ // the original violet gradient on the climb. The mark is the Pro summit, whose
22
+ // climb takes the theme's gradient.
23
+ const id = useId()
24
+ const svg = computed(() => {
25
+ const px = pixelSizes[size]
26
+ const drawing = variant === 'mark' ? markDrawing({ product: 'pro', size: px }) : faviconDrawing({ product: 'pro', size: px })
27
+ return drawingToSvg(drawing, { idPrefix: `brand-icon-${id}`, attrs: 'class="size-full" aria-hidden="true" focusable="false"' })
28
+ })
29
+ </script>
30
+
31
+ <template>
32
+ <!-- Trusted markup: drawingToSvg only interpolates the brand's own path data. -->
33
+ <span
34
+ class="relative inline-block shrink-0"
35
+ :class="[sizeClasses[size], variant === 'mark' ? 'text-highlighted' : '']"
36
+ aria-hidden="true"
37
+ v-html="svg"
38
+ />
39
+ </template>