@brumaombra/ui-vintage 0.8.2 → 0.9.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.
Files changed (134) hide show
  1. package/AGENTS.md +41 -0
  2. package/COMPONENTS.md +88 -17
  3. package/README.md +296 -262
  4. package/module.mjs +5 -11
  5. package/package.json +30 -12
  6. package/src/components/blog/BlogContentRenderer.vue +1 -1
  7. package/src/components/blog/BlogHeaderSection.vue +93 -19
  8. package/src/components/blog/BlogInfoSection.vue +39 -36
  9. package/src/components/blog/BlogPostHeader.vue +181 -0
  10. package/src/components/blog/BlogProgressBar.vue +3 -5
  11. package/src/components/blog/BlogSectionTitle.vue +8 -6
  12. package/src/components/blog/BlogTagsSection.vue +16 -7
  13. package/src/components/blog/CategoryCard.vue +18 -23
  14. package/src/components/blog/HeaderCarousel.vue +15 -6
  15. package/src/components/blog/PostCard.vue +13 -20
  16. package/src/components/blog/SocialShareSidebar.vue +24 -14
  17. package/src/components/blog/TableOfContents.vue +61 -28
  18. package/src/components/blog/index.ts +1 -0
  19. package/src/components/card-grid/CardGrid.vue +2 -2
  20. package/src/components/confirm-dialog/ConfirmDialog.vue +5 -0
  21. package/src/components/confirm-dialog/confirm-dialog-state.ts +22 -22
  22. package/src/components/content/BlogList.vue +28 -52
  23. package/src/components/content/BlogTable.vue +26 -30
  24. package/src/components/content/CodeBlock.vue +96 -22
  25. package/src/components/content/Flow.vue +29 -35
  26. package/src/components/content/ProseHr.vue +5 -5
  27. package/src/components/content/ProsePre.vue +9 -4
  28. package/src/components/content/Terminal.vue +42 -20
  29. package/src/components/cookie-consent/CookieConsent.vue +1 -1
  30. package/src/components/dashboard-shell/DashboardShell.vue +24 -7
  31. package/src/components/empty-state-card/EmptyStateCard.vue +2 -2
  32. package/src/components/error-page/ErrorPage.vue +3 -3
  33. package/src/components/info-card/InfoCard.vue +4 -4
  34. package/src/components/landing/landing-footer/LandingFooter.vue +16 -11
  35. package/src/components/landing/landing-navbar/LandingNavbar.vue +22 -6
  36. package/src/components/landing/landing-shell/LandingShell.vue +2 -7
  37. package/src/components/language-flag/index.ts +1 -0
  38. package/src/components/language-selector/LanguageSelector.vue +18 -10
  39. package/src/components/load-more-button/LoadMoreButton.vue +7 -12
  40. package/src/components/loading-state-card/LoadingStateCard.vue +2 -2
  41. package/src/components/message-dialog/MessageDialog.vue +11 -11
  42. package/src/components/message-dialog/message-dialog-state.ts +1 -4
  43. package/src/components/message-toast/MessageToast.vue +24 -9
  44. package/src/components/message-toast/message-toast-state.ts +20 -0
  45. package/src/components/page-header/PageHeader.vue +1 -1
  46. package/src/components/progress-component/ProgressComponent.vue +7 -3
  47. package/src/components/single-value-card/SingleValueCard.vue +37 -39
  48. package/src/components/slider-form-component/SliderFormComponent.vue +1 -1
  49. package/src/components/stat-strip/StatStrip.vue +36 -0
  50. package/src/components/stat-strip/index.ts +2 -0
  51. package/src/components/switch-form-component/SwitchFormComponent.vue +5 -5
  52. package/src/components/text-link/TextLink.vue +1 -1
  53. package/src/components/theme-selector/ThemeSelector.vue +2 -2
  54. package/src/components/theme-selector/theme-transition.ts +15 -3
  55. package/src/components/ui/accordion/Accordion.vue +18 -18
  56. package/src/components/ui/alert/AlertTitle.vue +1 -1
  57. package/src/components/ui/alert/index.ts +1 -1
  58. package/src/components/ui/avatar/AvatarFallback.vue +2 -1
  59. package/src/components/ui/avatar/AvatarGroup.vue +13 -3
  60. package/src/components/ui/breadcrumb/BreadcrumbEllipsis.vue +2 -2
  61. package/src/components/ui/busy/Busy.vue +5 -7
  62. package/src/components/ui/button/Button.vue +4 -2
  63. package/src/components/ui/button/index.ts +1 -4
  64. package/src/components/ui/calendar/Calendar.vue +12 -7
  65. package/src/components/ui/calendar/CalendarCell.vue +1 -1
  66. package/src/components/ui/calendar/CalendarCellTrigger.vue +1 -1
  67. package/src/components/ui/calendar/CalendarHeadCell.vue +1 -1
  68. package/src/components/ui/calendar/CalendarNextButton.vue +1 -1
  69. package/src/components/ui/calendar/CalendarPrevButton.vue +1 -1
  70. package/src/components/ui/card/Card.vue +5 -15
  71. package/src/components/ui/checkbox/Checkbox.vue +5 -1
  72. package/src/components/ui/combobox/ComboboxInput.vue +5 -1
  73. package/src/components/ui/command/CommandGroup.vue +17 -3
  74. package/src/components/ui/command/CommandInput.vue +1 -1
  75. package/src/components/ui/command/CommandList.vue +5 -1
  76. package/src/components/ui/data-table/DataTable.vue +11 -3
  77. package/src/components/ui/date-picker/DatePicker.vue +7 -1
  78. package/src/components/ui/date-time-picker/DateTimePicker.vue +5 -1
  79. package/src/components/ui/dropdown-menu/DropdownMenuItem.vue +1 -1
  80. package/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +1 -1
  81. package/src/components/ui/dropdown-menu/classes.ts +2 -0
  82. package/src/components/ui/field/Field.vue +4 -0
  83. package/src/components/ui/field/FieldLabel.vue +6 -2
  84. package/src/components/ui/field/field-context.ts +62 -0
  85. package/src/components/ui/field/index.ts +1 -0
  86. package/src/components/ui/file-dropzone/FileDropzone.vue +4 -4
  87. package/src/components/ui/input/Input.vue +5 -1
  88. package/src/components/ui/native-select/NativeSelect.vue +6 -2
  89. package/src/components/ui/number-field/NumberField.vue +5 -1
  90. package/src/components/ui/pagination/PaginationItem.vue +1 -1
  91. package/src/components/ui/pin-input/PinInputInput.vue +2 -2
  92. package/src/components/ui/progress/Progress.vue +2 -2
  93. package/src/components/ui/radio-group/RadioGroupCard.vue +1 -1
  94. package/src/components/ui/select/SelectItem.vue +1 -1
  95. package/src/components/ui/select/SelectItemContent.vue +1 -1
  96. package/src/components/ui/select/SelectTrigger.vue +5 -1
  97. package/src/components/ui/sidebar/Sidebar.vue +1 -1
  98. package/src/components/ui/sidebar/SidebarGroupAction.vue +1 -1
  99. package/src/components/ui/sidebar/SidebarGroupLabel.vue +1 -1
  100. package/src/components/ui/sidebar/SidebarInset.vue +1 -1
  101. package/src/components/ui/sidebar/SidebarMenuAction.vue +1 -1
  102. package/src/components/ui/sidebar/SidebarMenuBadge.vue +1 -1
  103. package/src/components/ui/sidebar/SidebarMenuSkeleton.vue +2 -2
  104. package/src/components/ui/sidebar/SidebarMenuSubButton.vue +1 -1
  105. package/src/components/ui/sidebar/SidebarProvider.vue +17 -6
  106. package/src/components/ui/sidebar/index.ts +2 -2
  107. package/src/components/ui/sidebar/utils.ts +1 -1
  108. package/src/components/ui/slider/Slider.vue +5 -4
  109. package/src/components/ui/spinner/Spinner.vue +3 -2
  110. package/src/components/ui/stepper/StepperDescription.vue +5 -3
  111. package/src/components/ui/stepper/StepperIndicator.vue +2 -2
  112. package/src/components/ui/stepper/StepperTitle.vue +5 -3
  113. package/src/components/ui/switch/Switch.vue +6 -2
  114. package/src/components/ui/tabs/TabsList.vue +1 -1
  115. package/src/components/ui/tabs/TabsTrigger.vue +1 -1
  116. package/src/components/ui/textarea/Textarea.vue +5 -1
  117. package/src/components/ui/time-picker/TimePicker.vue +2 -2
  118. package/src/components/ui/toggle-group/ToggleGroup.vue +1 -1
  119. package/src/components/ui/tooltip/TooltipContent.vue +1 -1
  120. package/src/i18n/de.json +16 -6
  121. package/src/i18n/en.json +16 -6
  122. package/src/i18n/es.json +16 -6
  123. package/src/i18n/fr.json +16 -6
  124. package/src/i18n/it.json +16 -6
  125. package/src/i18n/ja.json +16 -6
  126. package/src/i18n/pt.json +16 -6
  127. package/src/i18n/ru.json +16 -6
  128. package/src/i18n/zh.json +16 -6
  129. package/src/lib/color-tokens.ts +1 -1
  130. package/src/lib/i18n.ts +32 -4
  131. package/src/lib/utils.ts +21 -1
  132. package/src/runtime/ui-vintage-i18n-plugin.ts +11 -1
  133. package/src/styles.css +12 -16
  134. package/skills/ui-vintage/SKILL.md +0 -42
@@ -36,7 +36,7 @@ const props = defineProps<{
36
36
 
37
37
  <!-- Description -->
38
38
  <span v-if="props.description" data-slot="select-item-content-description" :class="cn(
39
- 'font-regular text-[11px] sm:text-xs leading-4 sm:leading-5 text-muted-foreground',
39
+ 'font-normal text-[11px] sm:text-xs leading-4 sm:leading-5 text-muted-foreground',
40
40
  props.descriptionClass
41
41
  )
42
42
  ">
@@ -6,6 +6,7 @@ import { ArrowDown01Icon } from '@hugeicons/core-free-icons';
6
6
  import { HugeiconsIcon } from '@hugeicons/vue';
7
7
  import { SelectIcon, SelectTrigger, useForwardProps } from 'reka-ui';
8
8
  import { cn } from '../../../lib/utils';
9
+ import { useFieldControlId } from '../field/field-context';
9
10
 
10
11
  // Props
11
12
  const props = withDefaults(defineProps<SelectTriggerProps & {
@@ -15,10 +16,13 @@ const props = withDefaults(defineProps<SelectTriggerProps & {
15
16
 
16
17
  const delegatedProps = reactiveOmit(props, 'class', 'size');
17
18
  const forwardedProps = useForwardProps(delegatedProps);
19
+
20
+ // Let a surrounding Field label point to this control
21
+ const fieldControlId = useFieldControlId();
18
22
  </script>
19
23
 
20
24
  <template>
21
- <SelectTrigger data-slot="select-trigger" :data-size="size" v-bind="forwardedProps" :class="cn(
25
+ <SelectTrigger :id="fieldControlId" data-slot="select-trigger" :data-size="size" v-bind="forwardedProps" :class="cn(
22
26
  'group/select-trigger uv-field flex w-fit cursor-pointer items-center justify-between gap-2 rounded border border-input bg-secondary px-4 py-3 text-xs font-semibold whitespace-nowrap text-foreground shadow-elevated-sm outline-none data-placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-60 data-[size=default]:h-13 data-[size=sm]:h-10 sm:text-sm',
23
27
  '*:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2',
24
28
  '[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4 [&_svg:not([class*=\'text-\'])]:text-muted-foreground',
@@ -78,7 +78,7 @@ function handleMobileSidebarClick(event: MouseEvent) {
78
78
  props.class
79
79
  )
80
80
  " v-bind="$attrs">
81
- <div data-sidebar="sidebar" class="bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm">
81
+ <div data-sidebar="sidebar" class="bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded group-data-[variant=floating]:border group-data-[variant=floating]:shadow-elevated-sm">
82
82
  <slot />
83
83
  </div>
84
84
  </div>
@@ -10,7 +10,7 @@ const props = defineProps<PrimitiveProps & { class?: HTMLAttributes['class'] }>(
10
10
 
11
11
  <template>
12
12
  <Primitive data-slot="sidebar-group-action" data-sidebar="group-action" :as="as" :as-child="asChild" :class="cn(
13
- 'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
13
+ 'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
14
14
  'after:absolute after:-inset-2 md:after:hidden',
15
15
  'group-data-[collapsible=icon]:hidden',
16
16
  props.class
@@ -10,7 +10,7 @@ const props = defineProps<PrimitiveProps & { class?: HTMLAttributes['class'] }>(
10
10
 
11
11
  <template>
12
12
  <Primitive data-slot="sidebar-group-label" data-sidebar="group-label" :as="as" :as-child="asChild" :class="cn(
13
- 'text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-semibold outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 group-data-[compact=true]/sidebar-wrapper:h-7 group-data-[compact=true]/sidebar-wrapper:px-1.5 [&>svg]:size-4 [&>svg]:shrink-0',
13
+ 'text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded px-2 text-xs font-semibold outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 group-data-[compact=true]/sidebar-wrapper:h-7 group-data-[compact=true]/sidebar-wrapper:px-1.5 [&>svg]:size-4 [&>svg]:shrink-0',
14
14
  'group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0',
15
15
  props.class
16
16
  )
@@ -11,7 +11,7 @@ const props = defineProps<{
11
11
  <template>
12
12
  <main data-slot="sidebar-inset" :class="cn(
13
13
  'bg-background relative flex w-full flex-1 flex-col',
14
- 'md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2',
14
+ 'md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded md:peer-data-[variant=inset]:shadow-elevated-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2',
15
15
  props.class
16
16
  )
17
17
  ">
@@ -15,7 +15,7 @@ const props = withDefaults(defineProps<PrimitiveProps & {
15
15
 
16
16
  <template>
17
17
  <Primitive data-slot="sidebar-menu-action" data-sidebar="menu-action" :class="cn(
18
- 'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
18
+ 'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
19
19
  'after:absolute after:-inset-2 md:after:hidden',
20
20
  'peer-data-[size=sm]/menu-button:top-1',
21
21
  'peer-data-[size=default]/menu-button:top-1.5',
@@ -10,7 +10,7 @@ const props = defineProps<{
10
10
 
11
11
  <template>
12
12
  <div data-slot="sidebar-menu-badge" data-sidebar="menu-badge" :class="cn(
13
- 'text-sidebar-foreground pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-semibold tabular-nums select-none',
13
+ 'text-sidebar-foreground pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded px-1 text-xs font-semibold tabular-nums select-none',
14
14
  'peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground',
15
15
  'peer-data-[size=sm]/menu-button:top-1',
16
16
  'peer-data-[size=default]/menu-button:top-1.5',
@@ -14,11 +14,11 @@ const width = '72%';
14
14
 
15
15
  <template>
16
16
  <div data-slot="sidebar-menu-skeleton" data-sidebar="menu-skeleton" :class="cn(
17
- 'flex h-8 items-center gap-2 rounded-md px-2 group-data-[compact=true]/sidebar-wrapper:h-7 group-data-[compact=true]/sidebar-wrapper:gap-1.5 group-data-[compact=true]/sidebar-wrapper:px-1.5',
17
+ 'flex h-8 items-center gap-2 rounded px-2 group-data-[compact=true]/sidebar-wrapper:h-7 group-data-[compact=true]/sidebar-wrapper:gap-1.5 group-data-[compact=true]/sidebar-wrapper:px-1.5',
18
18
  props.class
19
19
  )
20
20
  ">
21
- <Skeleton v-if="showIcon" class="size-4 rounded-md group-data-[compact=true]/sidebar-wrapper:size-3.5" data-sidebar="menu-skeleton-icon" />
21
+ <Skeleton v-if="showIcon" class="size-4 rounded group-data-[compact=true]/sidebar-wrapper:size-3.5" data-sidebar="menu-skeleton-icon" />
22
22
 
23
23
  <Skeleton class="h-4 max-w-(--skeleton-width) flex-1" data-sidebar="menu-skeleton-text" :style="{ '--skeleton-width': width }" />
24
24
  </div>
@@ -17,7 +17,7 @@ const props = withDefaults(defineProps<PrimitiveProps & {
17
17
 
18
18
  <template>
19
19
  <Primitive data-slot="sidebar-menu-sub-button" data-sidebar="menu-sub-button" :as="as" :as-child="asChild" :data-size="size" :data-active="isActive" :class="cn(
20
- 'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex min-h-7 min-w-0 -translate-x-px items-center gap-2 rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 group-data-[compact=true]/sidebar-wrapper:min-h-6 group-data-[compact=true]/sidebar-wrapper:gap-1.5 group-data-[compact=true]/sidebar-wrapper:px-1.5 group-data-[compact=true]/sidebar-wrapper:[&>svg]:size-3.5 [&>svg]:size-4 [&>svg]:shrink-0',
20
+ 'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex min-h-7 min-w-0 -translate-x-px items-center gap-2 rounded px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 group-data-[compact=true]/sidebar-wrapper:min-h-6 group-data-[compact=true]/sidebar-wrapper:gap-1.5 group-data-[compact=true]/sidebar-wrapper:px-1.5 group-data-[compact=true]/sidebar-wrapper:[&>svg]:size-3.5 [&>svg]:size-4 [&>svg]:shrink-0',
21
21
  'data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground',
22
22
  size === 'sm' && 'text-xs',
23
23
  size === 'md' &&
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { HTMLAttributes, Ref } from 'vue';
3
- import { defaultDocument, useEventListener, useMediaQuery, useVModel } from '@vueuse/core';
3
+ import { useEventListener, useMediaQuery, useMounted, useVModel } from '@vueuse/core';
4
+ import { useCookie } from 'nuxt/app';
4
5
  import { TooltipProvider } from 'reka-ui';
5
6
  import { computed, ref } from 'vue';
6
7
  import { cn } from '../../../lib/utils';
@@ -13,7 +14,7 @@ const props = withDefaults(defineProps<{
13
14
  compact?: boolean;
14
15
  class?: HTMLAttributes['class'];
15
16
  }>(), {
16
- defaultOpen: !defaultDocument?.cookie.includes(`${SIDEBAR_COOKIE_NAME}=false`),
17
+ defaultOpen: undefined,
17
18
  open: undefined,
18
19
  compact: false
19
20
  });
@@ -23,19 +24,29 @@ const emits = defineEmits<{
23
24
  'update:open': [open: boolean];
24
25
  }>();
25
26
 
26
- const isMobile = useMediaQuery('(max-width: 768px)');
27
+ // Remembered open state, read per instance on both server and client so the first render matches
28
+ const sidebarCookie = useCookie<boolean | null>(SIDEBAR_COOKIE_NAME, {
29
+ default: () => null,
30
+ maxAge: SIDEBAR_COOKIE_MAX_AGE,
31
+ path: '/'
32
+ });
33
+
34
+ // The mobile layout only exists in the browser: render the desktop markup until hydration is done
35
+ const isMounted = useMounted();
36
+ const matchesMobile = useMediaQuery('(max-width: 768px)');
37
+ const isMobile = computed(() => isMounted.value && matchesMobile.value);
27
38
  const openMobile = ref(false);
28
39
 
29
40
  const open = useVModel(props, 'open', emits, {
30
- defaultValue: props.defaultOpen ?? false,
41
+ defaultValue: props.defaultOpen ?? sidebarCookie.value !== false,
31
42
  passive: (props.open === undefined) as false
32
43
  }) as Ref<boolean>;
33
44
 
34
45
  function setOpen(value: boolean) {
35
46
  open.value = value; // emits('update:open', value)
36
47
 
37
- // This sets the cookie to keep the sidebar state.
38
- document.cookie = `${SIDEBAR_COOKIE_NAME}=${open.value}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
48
+ // Keep the sidebar state for the next visit
49
+ sidebarCookie.value = value;
39
50
  }
40
51
 
41
52
  function setOpenMobile(value: boolean) {
@@ -44,14 +44,14 @@ export type SidebarMenuButtonVariants = {
44
44
  export const sidebarMenuButtonVariants: (
45
45
  props?: SidebarMenuButtonVariants & ClassProp
46
46
  ) => string = cva(
47
- 'peer/menu-button flex w-full items-center gap-2 rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,min-height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-semibold data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! group-data-[compact=true]/sidebar-wrapper:gap-1.5 group-data-[compact=true]/sidebar-wrapper:p-2.5 group-data-[compact=true]/sidebar-wrapper:[&>svg]:size-3.5 [&>svg]:size-4 [&>svg]:shrink-0',
47
+ 'peer/menu-button flex w-full items-center gap-2 rounded p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,min-height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-semibold data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! group-data-[compact=true]/sidebar-wrapper:gap-1.5 group-data-[compact=true]/sidebar-wrapper:p-2.5 group-data-[compact=true]/sidebar-wrapper:[&>svg]:size-3.5 [&>svg]:size-4 [&>svg]:shrink-0',
48
48
  {
49
49
  variants: {
50
50
  variant: {
51
51
  default:
52
52
  'hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',
53
53
  outline:
54
- 'bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]'
54
+ 'bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_var(--sidebar-accent)]'
55
55
  },
56
56
  size: {
57
57
  default:
@@ -12,7 +12,7 @@ export const [useSidebar, provideSidebarContext] = createContext<{
12
12
  state: ComputedRef<'expanded' | 'collapsed'>;
13
13
  open: Ref<boolean>;
14
14
  setOpen: (value: boolean) => void;
15
- isMobile: Ref<boolean>;
15
+ isMobile: Readonly<Ref<boolean>>;
16
16
  openMobile: Ref<boolean>;
17
17
  setOpenMobile: (value: boolean) => void;
18
18
  toggleSidebar: () => void;
@@ -7,13 +7,14 @@ import { cn } from '../../../lib/utils';
7
7
 
8
8
  // Props
9
9
  const props = defineProps<SliderRootProps & {
10
+ thumbLabel?: string;
10
11
  class?: HTMLAttributes['class'];
11
12
  }>();
12
13
 
13
14
  // Emits
14
15
  const emits = defineEmits<SliderRootEmits>();
15
16
 
16
- const delegatedProps = reactiveOmit(props, 'class');
17
+ const delegatedProps = reactiveOmit(props, 'class', 'thumbLabel');
17
18
  const forwarded = useForwardPropsEmits(delegatedProps, emits);
18
19
  </script>
19
20
 
@@ -23,12 +24,12 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
23
24
  props.class
24
25
  )" v-bind="forwarded">
25
26
  <!-- Track -->
26
- <SliderTrack data-slot="slider-track" class="relative grow overflow-hidden rounded bg-muted transition-[height,width] duration-200 ease-spring data-[orientation=horizontal]:h-2 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-2 group-hover/slider:data-[orientation=horizontal]:h-2.5 group-hover/slider:data-[orientation=vertical]:w-2.5">
27
+ <SliderTrack data-slot="slider-track" class="relative grow overflow-hidden rounded bg-muted data-[orientation=horizontal]:h-2 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-2">
27
28
  <!-- Filled range -->
28
- <SliderRange data-slot="slider-range" class="absolute bg-primary bg-[linear-gradient(90deg,transparent,rgb(255_255_255/0.22))] data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full" />
29
+ <SliderRange data-slot="slider-range" class="absolute bg-primary data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full" />
29
30
  </SliderTrack>
30
31
 
31
32
  <!-- Thumbs -->
32
- <SliderThumb v-for="(_, key) in modelValue" :key="key" data-slot="slider-thumb" class="block size-5 shrink-0 cursor-grab rounded border-2 border-primary bg-white shadow-elevated-md outline-none [transition:scale_300ms_var(--ease-spring),box-shadow_200ms] hover:scale-110 hover:shadow-[0_0_0_6px_color-mix(in_oklab,var(--primary)_16%,transparent)] focus-visible:shadow-[0_0_0_6px_color-mix(in_oklab,var(--primary)_24%,transparent)] active:scale-125 active:cursor-grabbing disabled:pointer-events-none disabled:opacity-50" />
33
+ <SliderThumb v-for="(_, key) in modelValue" :key="key" data-slot="slider-thumb" :aria-label="props.thumbLabel" class="block size-5 shrink-0 cursor-grab rounded border-2 border-primary bg-card shadow-elevated-sm outline-none transition-[background-color,box-shadow] duration-150 hover:bg-[color-mix(in_oklab,var(--primary)_10%,var(--card))] focus-visible:ring-[3px] focus-visible:ring-ring/45 active:cursor-grabbing active:bg-[color-mix(in_oklab,var(--primary)_15%,var(--card))] disabled:pointer-events-none disabled:opacity-50" />
33
34
  </SliderRoot>
34
35
  </template>
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { HTMLAttributes } from 'vue';
3
3
  import { computed } from 'vue';
4
- import { getUiVintageRuntimeMessage } from '../../../lib/i18n';
4
+ import { useUiVintageMessage } from '../../../lib/i18n';
5
5
  import { cn } from '../../../lib/utils';
6
6
 
7
7
  // Props
@@ -14,7 +14,8 @@ const props = withDefaults(defineProps<{
14
14
  label: ''
15
15
  });
16
16
 
17
- const resolvedLabel = computed(() => props.label || getUiVintageRuntimeMessage('uiVintage.buttons.loading', 'Loading'));
17
+ const message = useUiVintageMessage();
18
+ const resolvedLabel = computed(() => props.label || message('uiVintage.buttons.loading', 'Loading'));
18
19
 
19
20
  // Size classes
20
21
  const sizeClasses: Record<NonNullable<typeof props.size>, string> = {
@@ -5,10 +5,12 @@ import { reactiveOmit } from '@vueuse/core';
5
5
  import { StepperDescription } from 'reka-ui';
6
6
  import { cn } from '../../../lib/utils';
7
7
 
8
- // Props
9
- const props = defineProps<StepperDescriptionProps & {
8
+ // Props (rendered as inline text by default: the stepper trigger is a button, which can't hold headings or paragraphs)
9
+ const props = withDefaults(defineProps<StepperDescriptionProps & {
10
10
  class?: HTMLAttributes['class'];
11
- }>();
11
+ }>(), {
12
+ as: 'span'
13
+ });
12
14
 
13
15
  const delegatedProps = reactiveOmit(props, 'class');
14
16
  </script>
@@ -22,10 +22,10 @@ const state = computed(() => itemContext.state.value);
22
22
 
23
23
  <template>
24
24
  <StepperIndicator v-slot="{ step }" data-slot="stepper-indicator" :data-state="state" v-bind="delegatedProps" :class="cn(
25
- 'relative inline-flex size-9 shrink-0 items-center justify-center rounded-full border-2 border-border bg-card text-sm font-bold text-muted-foreground ring-0 ring-primary/15 tabular-nums',
25
+ 'relative inline-flex size-9 shrink-0 items-center justify-center rounded border border-border bg-card text-xs font-semibold text-muted-foreground tabular-nums',
26
26
  '[transition:color_150ms,background-color_220ms,border-color_220ms,box-shadow_380ms_var(--ease-spring),transform_380ms_var(--ease-spring)]',
27
27
  'group-hover/stepper-trigger:border-border-strong group-active/stepper-trigger:scale-95 group-active/stepper-trigger:duration-75',
28
- 'data-[state=active]:border-primary data-[state=active]:text-primary data-[state=active]:ring-4 data-[state=active]:group-hover/stepper-trigger:border-primary',
28
+ 'data-[state=active]:border-primary data-[state=active]:bg-primary/10 data-[state=active]:text-primary data-[state=active]:group-hover/stepper-trigger:border-primary',
29
29
  'data-[state=completed]:border-primary data-[state=completed]:bg-primary data-[state=completed]:text-primary-foreground data-[state=completed]:group-hover/stepper-trigger:border-primary',
30
30
  '[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4',
31
31
  props.class
@@ -5,10 +5,12 @@ import { reactiveOmit } from '@vueuse/core';
5
5
  import { StepperTitle } from 'reka-ui';
6
6
  import { cn } from '../../../lib/utils';
7
7
 
8
- // Props
9
- const props = defineProps<StepperTitleProps & {
8
+ // Props (rendered as inline text by default: the stepper trigger is a button, which can't hold headings or paragraphs)
9
+ const props = withDefaults(defineProps<StepperTitleProps & {
10
10
  class?: HTMLAttributes['class'];
11
- }>();
11
+ }>(), {
12
+ as: 'span'
13
+ });
12
14
 
13
15
  const delegatedProps = reactiveOmit(props, 'class');
14
16
  </script>
@@ -4,6 +4,7 @@ import type { HTMLAttributes } from 'vue';
4
4
  import { reactiveOmit } from '@vueuse/core';
5
5
  import { SwitchRoot, SwitchThumb, useForwardPropsEmits } from 'reka-ui';
6
6
  import { cn } from '../../../lib/utils';
7
+ import { useFieldControlId } from '../field/field-context';
7
8
 
8
9
  // Props
9
10
  const props = defineProps<SwitchRootProps & {
@@ -15,13 +16,16 @@ const emits = defineEmits<SwitchRootEmits>();
15
16
 
16
17
  const delegatedProps = reactiveOmit(props, 'class');
17
18
  const forwarded = useForwardPropsEmits(delegatedProps, emits);
19
+
20
+ // Let a surrounding Field label point to this control
21
+ const fieldControlId = useFieldControlId(() => props.id);
18
22
  </script>
19
23
 
20
24
  <template>
21
- <SwitchRoot v-slot="slotProps" data-slot="switch" v-bind="forwarded" :class="cn(
25
+ <SwitchRoot v-slot="slotProps" data-slot="switch" v-bind="forwarded" :id="fieldControlId" :class="cn(
22
26
  'group/switch peer relative inline-flex h-7 w-12 shrink-0 cursor-pointer items-center rounded border border-input bg-secondary shadow-elevated-sm outline-none',
23
27
  '[transition:background-color_200ms,border-color_200ms,box-shadow_250ms] hover:border-border-strong focus-visible:ring-[3px] focus-visible:ring-ring/45 disabled:cursor-not-allowed disabled:opacity-60',
24
- 'data-[state=checked]:border-primary data-[state=checked]:bg-accent data-[state=checked]:shadow-[0_0_0_3px_color-mix(in_oklab,var(--primary)_14%,transparent)]',
28
+ 'data-[state=checked]:border-primary data-[state=checked]:bg-accent',
25
29
  props.class
26
30
  )">
27
31
  <SwitchThumb data-slot="switch-thumb" :class="cn(
@@ -21,7 +21,7 @@ const delegatedProps = reactiveOmit(props, 'class', 'indicatorClass');
21
21
  )">
22
22
  <!-- Sliding active indicator -->
23
23
  <TabsIndicator data-slot="tabs-indicator" :class="cn(
24
- 'absolute -z-10 rounded border border-primary bg-surface shadow-elevated-sm transition-[translate,width,height] duration-[380ms] ease-spring',
24
+ 'absolute -z-10 rounded border border-primary/40 bg-primary/10 transition-[translate,width,height] duration-[380ms] ease-spring',
25
25
  'group-aria-[orientation=horizontal]/tabs-list:inset-y-1 group-aria-[orientation=horizontal]/tabs-list:left-0 group-aria-[orientation=horizontal]/tabs-list:w-(--reka-tabs-indicator-size) group-aria-[orientation=horizontal]/tabs-list:translate-x-(--reka-tabs-indicator-position)',
26
26
  'group-aria-[orientation=vertical]/tabs-list:inset-x-1 group-aria-[orientation=vertical]/tabs-list:top-0 group-aria-[orientation=vertical]/tabs-list:h-(--reka-tabs-indicator-size) group-aria-[orientation=vertical]/tabs-list:translate-y-(--reka-tabs-indicator-position)',
27
27
  props.indicatorClass
@@ -19,7 +19,7 @@ const forwardedProps = useForwardProps(delegatedProps);
19
19
  'relative inline-flex min-h-9 flex-1 cursor-pointer items-center justify-center gap-1.5 rounded border border-transparent bg-transparent px-4 py-2 text-sm font-semibold whitespace-nowrap text-muted-foreground outline-none select-none',
20
20
  '[transition:color_150ms,background-color_150ms,scale_300ms_var(--ease-spring)] hover:text-foreground active:scale-[0.97] focus-visible:ring-[3px] focus-visible:ring-ring/45 disabled:pointer-events-none disabled:opacity-50',
21
21
  'data-[state=active]:text-primary data-[state=inactive]:hover:bg-accent/60',
22
- '[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4 [&_svg]:transition-transform [&_svg]:duration-300 [&_svg]:ease-bounce data-[state=active]:[&_svg]:scale-110',
22
+ '[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4',
23
23
  props.class
24
24
  )" v-bind="forwardedProps">
25
25
  <slot />
@@ -2,6 +2,7 @@
2
2
  import type { HTMLAttributes } from 'vue';
3
3
  import { useVModel } from '@vueuse/core';
4
4
  import { cn } from '../../../lib/utils';
5
+ import { useFieldControlId } from '../field/field-context';
5
6
 
6
7
  // Props
7
8
  const props = defineProps<{
@@ -19,10 +20,13 @@ const modelValue = useVModel(props, 'modelValue', emits, {
19
20
  passive: true,
20
21
  defaultValue: props.defaultValue
21
22
  });
23
+
24
+ // Let a surrounding Field label point to this control
25
+ const fieldControlId = useFieldControlId();
22
26
  </script>
23
27
 
24
28
  <template>
25
- <textarea v-model="modelValue" data-slot="textarea" :class="cn(
29
+ <textarea :id="fieldControlId" v-model="modelValue" data-slot="textarea" :class="cn(
26
30
  'uv-field flex field-sizing-content min-h-30 w-full min-w-0 rounded border border-input bg-secondary px-4 py-3 text-xs font-semibold text-foreground shadow-elevated-sm outline-none sm:text-sm',
27
31
  'selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground placeholder:opacity-60',
28
32
  'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-60 aria-invalid:animate-uv-shake',
@@ -16,7 +16,7 @@ const props = withDefaults(defineProps<{
16
16
  inputClass?: HTMLAttributes['class'];
17
17
  }>(), {
18
18
  defaultValue: '',
19
- step: 1,
19
+ step: 60,
20
20
  disabled: false
21
21
  });
22
22
 
@@ -43,7 +43,7 @@ const time = computed({
43
43
 
44
44
  <template>
45
45
  <Input v-model="time" type="time" :step="props.step" :min="props.min" :max="props.max" :disabled="props.disabled" :class="cn(
46
- 'cursor-pointer font-normal hover:border-(--border-hover-light) hover:bg-(--bg-selected-light) dark:hover:border-(--border-hover-dark) dark:hover:bg-(--bg-selected-dark) [&::-webkit-calendar-picker-indicator]:cursor-pointer [&::-webkit-calendar-picker-indicator]:opacity-70 hover:[&::-webkit-calendar-picker-indicator]:opacity-100',
46
+ 'cursor-pointer font-normal hover:border-border-strong hover:bg-accent [&::-webkit-calendar-picker-indicator]:cursor-pointer [&::-webkit-calendar-picker-indicator]:opacity-70 hover:[&::-webkit-calendar-picker-indicator]:opacity-100',
47
47
  props.inputClass,
48
48
  props.class
49
49
  )
@@ -85,7 +85,7 @@ const indicatorStyle = computed<CSSProperties>(() => ({
85
85
  <span v-if="isSingle" data-slot="toggle-group-indicator" aria-hidden="true" :style="indicatorStyle" :class="cn(
86
86
  'pointer-events-none absolute top-0 left-0 z-0 rounded-sm border',
87
87
  animated && '[transition:transform_380ms_var(--ease-spring),width_380ms_var(--ease-spring),height_380ms_var(--ease-spring),opacity_180ms_var(--ease-snappy)]',
88
- props.highlight ? 'border-primary/40 bg-primary/10 shadow-[0_0_0_3px_color-mix(in_oklab,var(--primary)_10%,transparent)]' : 'border-border-strong bg-surface shadow-elevated-sm'
88
+ props.highlight ? 'border-primary/40 bg-primary/10' : 'border-border-strong bg-surface shadow-elevated-sm'
89
89
  )" />
90
90
 
91
91
  <slot v-bind="slotProps" />
@@ -26,7 +26,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
26
26
  <template>
27
27
  <TooltipPortal>
28
28
  <TooltipContent data-slot="tooltip-content" v-bind="{ ...forwarded, ...$attrs }" :class="cn(
29
- 'uv-floating-motion bg-foreground text-background z-50 w-fit max-w-72 rounded-md px-3 py-1.5 text-xs font-semibold text-balance shadow-elevated-lg',
29
+ 'uv-floating-motion bg-foreground text-background z-50 w-fit max-w-72 rounded px-3 py-1.5 text-xs font-semibold text-balance shadow-elevated-lg',
30
30
  props.class
31
31
  )
32
32
  ">
package/src/i18n/de.json CHANGED
@@ -3,6 +3,7 @@
3
3
  "common": {
4
4
  "breadcrumb": "Brotkrumennavigation",
5
5
  "more": "Mehr",
6
+ "home": "Startseite",
6
7
  "empty": {
7
8
  "title": "Hier gibt es noch nichts",
8
9
  "description": "Zurzeit gibt es keine Inhalte zum Anzeigen."
@@ -10,7 +11,8 @@
10
11
  "loading": {
11
12
  "title": "Wird geladen...",
12
13
  "description": "Bitte warte einen Moment, wahrend wir die Inhalte laden."
13
- }
14
+ },
15
+ "logo": "{name}-Logo"
14
16
  },
15
17
  "buttons": {
16
18
  "remove": "Entfernen",
@@ -50,7 +52,10 @@
50
52
  "description": "Nach einem auszuführenden Befehl suchen..."
51
53
  },
52
54
  "datePicker": {
53
- "placeholder": "Datum auswählen"
55
+ "placeholder": "Datum auswählen",
56
+ "month": "Monat",
57
+ "year": "Jahr",
58
+ "time": "Uhrzeit"
54
59
  },
55
60
  "terminal": {
56
61
  "title": "Terminal",
@@ -60,7 +65,9 @@
60
65
  "code": {
61
66
  "title": "Code",
62
67
  "copy": "Code kopieren",
63
- "copied": "Code kopiert"
68
+ "copied": "Code kopiert",
69
+ "position": "Zeile {line}, Spalte {column}",
70
+ "plainText": "Nur-Text"
64
71
  },
65
72
  "footer": {
66
73
  "madeWith": "Gemacht mit",
@@ -71,14 +78,20 @@
71
78
  "readingProgress": "Lesefortschritt",
72
79
  "readMore": "Weiterlesen",
73
80
  "goToPost": "Zum Beitrag",
81
+ "new": "Neu",
74
82
  "tableOfContents": "Inhaltsverzeichnis",
75
83
  "tableOfContentsDescription": "Zu einem Abschnitt dieses Artikels springen",
84
+ "tableOfContentsSections": "{count} Abschnitt | {count} Abschnitte",
76
85
  "openTableOfContents": "Inhaltsverzeichnis offnen",
77
86
  "closeTableOfContents": "Inhaltsverzeichnis schliessen",
78
87
  "loadMoreWithNumbers": "Mehr laden ({current}/{total})",
79
88
  "author": "Autor",
80
89
  "publishedDate": "Veroffentlicht",
81
90
  "lastUpdatedDate": "Aktualisiert",
91
+ "readingTime": "Lesezeit",
92
+ "readingTimeValue": "{minutes} Min.",
93
+ "category": "Kategorie",
94
+ "breadcrumbBlog": "Blog",
82
95
  "carouselNavigation": "Karussellnavigation",
83
96
  "goToSlide": "Zur Folie {index} wechseln",
84
97
  "previousSlide": "Vorherige Folie",
@@ -97,9 +110,6 @@
97
110
  "copied": "Link kopiert"
98
111
  }
99
112
  },
100
- "select": {
101
- "defaultPlaceholder": "Option auswahlen"
102
- },
103
113
  "errorPage": {
104
114
  "notFound": {
105
115
  "title": "Seite nicht gefunden",
package/src/i18n/en.json CHANGED
@@ -3,6 +3,7 @@
3
3
  "common": {
4
4
  "breadcrumb": "Breadcrumb",
5
5
  "more": "More",
6
+ "home": "Home",
6
7
  "empty": {
7
8
  "title": "Nothing here yet",
8
9
  "description": "There is no content to show right now."
@@ -10,7 +11,8 @@
10
11
  "loading": {
11
12
  "title": "Loading...",
12
13
  "description": "Please wait a moment while we load the content."
13
- }
14
+ },
15
+ "logo": "{name} logo"
14
16
  },
15
17
  "buttons": {
16
18
  "remove": "Remove",
@@ -50,7 +52,10 @@
50
52
  "description": "Search for a command to run..."
51
53
  },
52
54
  "datePicker": {
53
- "placeholder": "Pick a date"
55
+ "placeholder": "Pick a date",
56
+ "month": "Month",
57
+ "year": "Year",
58
+ "time": "Time"
54
59
  },
55
60
  "terminal": {
56
61
  "title": "Terminal",
@@ -60,7 +65,9 @@
60
65
  "code": {
61
66
  "title": "Code",
62
67
  "copy": "Copy code",
63
- "copied": "Code copied"
68
+ "copied": "Code copied",
69
+ "position": "Ln {line}, Col {column}",
70
+ "plainText": "Plain text"
64
71
  },
65
72
  "footer": {
66
73
  "madeWith": "Made with",
@@ -71,14 +78,20 @@
71
78
  "readingProgress": "Reading progress",
72
79
  "readMore": "Read more",
73
80
  "goToPost": "Go to post",
81
+ "new": "New",
74
82
  "tableOfContents": "Table of contents",
75
83
  "tableOfContentsDescription": "Jump to a section of this article",
84
+ "tableOfContentsSections": "{count} section | {count} sections",
76
85
  "openTableOfContents": "Open table of contents",
77
86
  "closeTableOfContents": "Close table of contents",
78
87
  "loadMoreWithNumbers": "Load more ({current}/{total})",
79
88
  "author": "Author",
80
89
  "publishedDate": "Published",
81
90
  "lastUpdatedDate": "Updated",
91
+ "readingTime": "Reading time",
92
+ "readingTimeValue": "{minutes} min",
93
+ "category": "Category",
94
+ "breadcrumbBlog": "Blog",
82
95
  "carouselNavigation": "Carousel navigation",
83
96
  "goToSlide": "Go to slide {index}",
84
97
  "previousSlide": "Previous slide",
@@ -97,9 +110,6 @@
97
110
  "copied": "Link copied"
98
111
  }
99
112
  },
100
- "select": {
101
- "defaultPlaceholder": "Select an option"
102
- },
103
113
  "errorPage": {
104
114
  "notFound": {
105
115
  "title": "Page not found",