@poveste/app 0.3.0 → 0.3.2

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.
@@ -54,6 +54,32 @@
54
54
  }
55
55
  }
56
56
  @scope (:root) to (.__poveste-render-story:not(.__poveste-render-custom-controls)) {
57
+ /* Outside the layer — registration is document-global, not cascaded. */
58
+ @property --poveste-count {
59
+ syntax: '<integer>';
60
+ initial-value: 0;
61
+ inherits: false;
62
+ }
63
+ /* Layered so a `transition-none` utility can still win — see #104. */
64
+ @layer components {
65
+ .poveste-home-counter-value {
66
+ &[data-v-5760c084] {
67
+ counter-reset: poveste-count var(--poveste-count, 0);
68
+
69
+ /* Opt in, so an engine without the query animates nothing. */
70
+ }
71
+ &[data-v-5760c084]::after {
72
+ content: counter(poveste-count);
73
+ }
74
+ @media (prefers-reduced-motion: no-preference) {
75
+ &[data-v-5760c084] {
76
+ transition: --poveste-count 700ms ease-out 100ms;
77
+ }
78
+ }
79
+ }
80
+ }
81
+ }
82
+ @scope (:root) to (.__poveste-render-story:not(.__poveste-render-custom-controls)) {
57
83
  .base-empty[data-v-57f8bebf]:not(.no-animation) {
58
84
  animation: base-empty-57f8bebf .15s .2s both;
59
85
  }
@@ -6,7 +6,11 @@ import { computed, createElementBlock, createElementVNode, createVNode, defineCo
6
6
  //#region src/app/components/HomeView.vue?vue&type=script&setup=true&lang.ts
7
7
  var _hoisted_1 = { class: "poveste-home-view flex md:flex-col gap-12 items-center justify-center h-full" };
8
8
  var _hoisted_2 = ["src"];
9
- var _hoisted_3 = { class: "flex !md:flex-col flex-wrap justify-evenly gap-2 px-4 py-2 bg-gray-100 dark:bg-gray-750 rounded border border-gray-500/30" };
9
+ var _hoisted_3 = {
10
+ role: "group",
11
+ "aria-label": "Book contents",
12
+ class: "flex !md:flex-col flex-wrap justify-evenly gap-2 px-4 py-2 bg-gray-100 dark:bg-gray-750 rounded border border-gray-500/30"
13
+ };
10
14
  var HomeView_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
11
15
  __name: "HomeView",
12
16
  setup(__props) {
@@ -1,5 +1,7 @@
1
+ import _plugin_vue_export_helper_default from "../../_virtual/_plugin-vue_export-helper.js";
1
2
  import HomeCounter_vue_vue_type_script_setup_true_lang_default from "./HomeCounter.vue_vue_type_script_setup_true_lang.js";
3
+ /* empty css */
2
4
  //#region src/app/components/app/HomeCounter.vue
3
- var HomeCounter_default = HomeCounter_vue_vue_type_script_setup_true_lang_default;
5
+ var HomeCounter_default = /*#__PURE__*/ _plugin_vue_export_helper_default(HomeCounter_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-5760c084"]]);
4
6
  //#endregion
5
7
  export { HomeCounter_default as default };
@@ -1,10 +1,10 @@
1
- import { createElementBlock, createElementVNode, createVNode, defineComponent, openBlock, toDisplayString, unref } from "@poveste/vendors/vue";
1
+ import { createElementBlock, createElementVNode, createVNode, defineComponent, nextTick, normalizeStyle, onMounted, openBlock, ref, toDisplayString, unref, useTemplateRef, watch } from "@poveste/vendors/vue";
2
2
  import { Icon } from "@poveste/vendors/iconify";
3
3
  //#region src/app/components/app/HomeCounter.vue?vue&type=script&setup=true&lang.ts
4
- var _hoisted_1 = { class: "p-2 flex items-center gap-x-2" };
5
- var _hoisted_2 = { class: "flex flex-col leading-none" };
6
- var _hoisted_3 = { class: "text-primary-500 min-w-[80px] font-bold" };
7
- var _hoisted_4 = { class: "text-sm text-gray-900 dark:text-gray-100" };
4
+ var _hoisted_1 = { class: "poveste-home-counter p-2 flex items-center gap-x-2" };
5
+ var _hoisted_2 = { class: "flex flex-col leading-none m-0" };
6
+ var _hoisted_3 = { class: "order-2 text-sm text-gray-900 dark:text-gray-100" };
7
+ var _hoisted_4 = { class: "sr-only ms-0" };
8
8
  var HomeCounter_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
9
9
  __name: "HomeCounter",
10
10
  props: {
@@ -23,11 +23,31 @@ var HomeCounter_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defi
23
23
  },
24
24
  setup(__props) {
25
25
  const props = __props;
26
+ const displayCount = ref(0);
27
+ const valueEl = useTemplateRef("valueEl");
28
+ onMounted(async () => {
29
+ await nextTick();
30
+ valueEl.value?.offsetWidth;
31
+ displayCount.value = props.count;
32
+ });
33
+ watch(() => props.count, (value) => {
34
+ displayCount.value = value;
35
+ });
26
36
  return (_ctx, _cache) => {
27
37
  return openBlock(), createElementBlock("div", _hoisted_1, [createVNode(unref(Icon), {
28
38
  icon: props.icon,
29
39
  class: "text-2xl text-gray-700 dark:text-gray-300 flex-none"
30
- }, null, 8, ["icon"]), createElementVNode("div", _hoisted_2, [createElementVNode("span", _hoisted_3, toDisplayString(__props.count), 1), createElementVNode("span", _hoisted_4, toDisplayString(__props.title), 1)])]);
40
+ }, null, 8, ["icon"]), createElementVNode("dl", _hoisted_2, [
41
+ createElementVNode("dt", _hoisted_3, toDisplayString(__props.title), 1),
42
+ createElementVNode("dd", {
43
+ ref_key: "valueEl",
44
+ ref: valueEl,
45
+ class: "poveste-home-counter-value order-1 ms-0 text-primary-500 min-w-[80px] font-bold",
46
+ style: normalizeStyle({ "--poveste-count": displayCount.value }),
47
+ "aria-hidden": "true"
48
+ }, null, 4),
49
+ createElementVNode("dd", _hoisted_4, toDisplayString(__props.count), 1)
50
+ ])]);
31
51
  };
32
52
  }
33
53
  });
package/dist/style.css CHANGED
@@ -424,6 +424,17 @@
424
424
  .visible {
425
425
  visibility: visible;
426
426
  }
427
+ .sr-only {
428
+ position: absolute;
429
+ width: 1px;
430
+ height: 1px;
431
+ padding: 0;
432
+ margin: -1px;
433
+ overflow: hidden;
434
+ clip-path: inset(50%);
435
+ white-space: nowrap;
436
+ border-width: 0;
437
+ }
427
438
  .absolute {
428
439
  position: absolute;
429
440
  }
@@ -493,6 +504,12 @@
493
504
  .z-100 {
494
505
  z-index: 100;
495
506
  }
507
+ .order-1 {
508
+ order: 1;
509
+ }
510
+ .order-2 {
511
+ order: 2;
512
+ }
496
513
  .container {
497
514
  width: 100%;
498
515
  @media (width >= 40rem) {
@@ -547,6 +564,9 @@
547
564
  .my-8 {
548
565
  margin-block: calc(var(--spacing) * 8);
549
566
  }
567
+ .ms-0 {
568
+ margin-inline-start: 0px;
569
+ }
550
570
  .prose {
551
571
  color: var(--tw-prose-body);
552
572
  max-width: 65ch;
@@ -995,6 +1015,9 @@
995
1015
  .block {
996
1016
  display: block;
997
1017
  }
1018
+ .contents {
1019
+ display: contents;
1020
+ }
998
1021
  .flex {
999
1022
  display: flex;
1000
1023
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@poveste/app",
3
3
  "type": "module",
4
- "version": "0.3.0",
4
+ "version": "0.3.2",
5
5
  "description": "Pre-bundled UI for Poveste",
6
6
  "author": {
7
7
  "name": "Sorin Gitlan"
@@ -30,9 +30,9 @@
30
30
  "dependencies": {
31
31
  "fuse.js": "^7.0.0",
32
32
  "shiki": "^3.0.0",
33
- "@poveste/controls": "^0.3.0",
34
- "@poveste/shared": "^0.3.0",
35
- "@poveste/vendors": "^0.3.0"
33
+ "@poveste/shared": "^0.3.2",
34
+ "@poveste/controls": "^0.3.2",
35
+ "@poveste/vendors": "^0.3.2"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@tailwindcss/postcss": "^4.3.3",
@@ -40,7 +40,12 @@ const stats = computed(() => {
40
40
  alt="Logo"
41
41
  class="w-64 h-64 opacity-25 mb-8 hidden md:block"
42
42
  >
43
- <div class="flex !md:flex-col flex-wrap justify-evenly gap-2 px-4 py-2 bg-gray-100 dark:bg-gray-750 rounded border border-gray-500/30">
43
+ <!-- Not a live region: counts move repeatedly while stories are collected. -->
44
+ <div
45
+ role="group"
46
+ aria-label="Book contents"
47
+ class="flex !md:flex-col flex-wrap justify-evenly gap-2 px-4 py-2 bg-gray-100 dark:bg-gray-750 rounded border border-gray-500/30"
48
+ >
44
49
  <HomeCounter
45
50
  title="Stories"
46
51
  icon="carbon:cube"
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { Icon } from '@iconify/vue'
3
+ import { nextTick, onMounted, ref, useTemplateRef, watch } from 'vue'
3
4
 
4
5
  const props = defineProps({
5
6
  icon: {
@@ -15,25 +16,70 @@ const props = defineProps({
15
16
  default: 0,
16
17
  },
17
18
  })
19
+
20
+ // Starts at 0 so the transition has a value to animate from.
21
+ const displayCount = ref(0)
22
+ const valueEl = useTemplateRef<HTMLElement>('valueEl')
23
+
24
+ onMounted(async () => {
25
+ await nextTick()
26
+ // Forced read, not rAF: rAF never fires in a background tab, leaving the
27
+ // count at 0. This resolves the starting value whether or not we're visible.
28
+ void valueEl.value?.offsetWidth
29
+ displayCount.value = props.count
30
+ })
31
+
32
+ watch(() => props.count, (value) => {
33
+ displayCount.value = value
34
+ })
18
35
  </script>
19
36
 
20
37
  <template>
21
- <div class="p-2 flex items-center gap-x-2">
38
+ <div class="poveste-home-counter p-2 flex items-center gap-x-2">
22
39
  <Icon
23
40
  :icon="props.icon"
24
41
  class="text-2xl text-gray-700 dark:text-gray-300 flex-none"
25
42
  />
26
- <div class="flex flex-col leading-none">
27
- <span
28
- class="text-primary-500 min-w-[80px] font-bold"
29
- >
30
- {{ count }}
31
- </span>
32
- <span
33
- class="text-sm text-gray-900 dark:text-gray-100"
34
- >
43
+ <!-- `order` keeps the number on top without putting `dd` before its `dt`. -->
44
+ <dl class="flex flex-col leading-none m-0">
45
+ <dt class="order-2 text-sm text-gray-900 dark:text-gray-100">
35
46
  {{ title }}
36
- </span>
37
- </div>
47
+ </dt>
48
+ <!-- Generated content, so hidden from AT; the real value follows. -->
49
+ <dd
50
+ ref="valueEl"
51
+ class="poveste-home-counter-value order-1 ms-0 text-primary-500 min-w-[80px] font-bold"
52
+ :style="{ '--poveste-count': displayCount }"
53
+ aria-hidden="true"
54
+ />
55
+ <dd class="sr-only ms-0">
56
+ {{ count }}
57
+ </dd>
58
+ </dl>
38
59
  </div>
39
60
  </template>
61
+
62
+ <style scoped>
63
+ /* Outside the layer — registration is document-global, not cascaded. */
64
+ @property --poveste-count {
65
+ syntax: '<integer>';
66
+ initial-value: 0;
67
+ inherits: false;
68
+ }
69
+
70
+ /* Layered so a `transition-none` utility can still win — see #104. */
71
+ @layer components {
72
+ .poveste-home-counter-value {
73
+ counter-reset: poveste-count var(--poveste-count, 0);
74
+
75
+ &::after {
76
+ content: counter(poveste-count);
77
+ }
78
+
79
+ /* Opt in, so an engine without the query animates nothing. */
80
+ @media (prefers-reduced-motion: no-preference) {
81
+ transition: --poveste-count 700ms ease-out 100ms;
82
+ }
83
+ }
84
+ }
85
+ </style>