@codenhub/styles 0.6.0 → 0.7.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 (113) hide show
  1. package/NOTICE +18 -16
  2. package/README.es.md +61 -0
  3. package/README.md +21 -16
  4. package/README.pt-BR.md +61 -0
  5. package/dist/aesthetics/chunky-tile.css +1 -1
  6. package/dist/aesthetics/cyber.css +1 -1
  7. package/dist/aesthetics/glass.css +1 -1
  8. package/dist/aesthetics/index.css +2 -2
  9. package/dist/aesthetics/neobrutalism.css +1 -1
  10. package/dist/aesthetics/pixel.css +1 -1
  11. package/dist/aesthetics/sketch.css +1 -1
  12. package/dist/components.css +2 -2
  13. package/dist/index.css +2 -2
  14. package/dist/native.css +2 -2
  15. package/dist/palette.css +1 -1
  16. package/dist/theme.css +2 -2
  17. package/dist/tw/aesthetics/chunky-tile.css +81 -23
  18. package/dist/tw/aesthetics/cyber.css +86 -29
  19. package/dist/tw/aesthetics/glass.css +177 -50
  20. package/dist/tw/aesthetics/neobrutalism.css +98 -22
  21. package/dist/tw/aesthetics/pixel.css +93 -8
  22. package/dist/tw/aesthetics/sketch.css +87 -22
  23. package/dist/tw/box.css +21 -2
  24. package/dist/tw/components/feedback.css +2 -2
  25. package/dist/tw/components/form.css +58 -19
  26. package/dist/tw/components/index.css +3 -4
  27. package/dist/tw/components/surface.css +15 -6
  28. package/dist/tw/index.css +1 -1
  29. package/dist/tw/intent.css +6 -5
  30. package/dist/tw/native.css +8 -8
  31. package/dist/tw/palette.css +6 -6
  32. package/dist/tw/reset.css +15 -15
  33. package/dist/tw/theme.css +29 -17
  34. package/dist/tw/utilities/content.css +43 -1
  35. package/dist/tw/utilities/layout.css +26 -12
  36. package/docs/accessibility.md +89 -60
  37. package/docs/changelog/0.1.1.md +1 -0
  38. package/docs/changelog/0.2.0.md +1 -0
  39. package/docs/changelog/0.3.0.md +1 -0
  40. package/docs/changelog/0.4.0.md +1 -0
  41. package/docs/changelog/0.5.0.md +8 -3
  42. package/docs/changelog/0.6.0.md +2 -1
  43. package/docs/changelog/0.7.0.md +50 -0
  44. package/docs/changelog/index.md +1 -0
  45. package/docs/concepts.md +188 -46
  46. package/docs/es/accessibility.md +150 -0
  47. package/docs/es/concepts.md +212 -0
  48. package/docs/es/index.md +104 -0
  49. package/docs/es/integrating/astro.md +35 -0
  50. package/docs/es/integrating/index.md +47 -0
  51. package/docs/es/integrating/nextjs.md +67 -0
  52. package/docs/es/integrating/svelte.md +45 -0
  53. package/docs/es/integrating/tailwind.md +78 -0
  54. package/docs/es/integrating/vue.md +31 -0
  55. package/docs/es/integrating/without-a-bundler.md +115 -0
  56. package/docs/es/setup.md +180 -0
  57. package/docs/es/usage/aesthetics.md +276 -0
  58. package/docs/es/usage/buttons.md +131 -0
  59. package/docs/es/usage/composing.md +164 -0
  60. package/docs/es/usage/content-and-layout.md +215 -0
  61. package/docs/es/usage/customizing.md +298 -0
  62. package/docs/es/usage/feedback.md +153 -0
  63. package/docs/es/usage/forms.md +231 -0
  64. package/docs/es/usage/index.md +29 -0
  65. package/docs/es/usage/solo.md +84 -0
  66. package/docs/es/usage/theming.md +296 -0
  67. package/docs/es/usage/tooltips.md +127 -0
  68. package/docs/index.md +85 -26
  69. package/docs/integrating/astro.md +3 -4
  70. package/docs/integrating/index.md +18 -13
  71. package/docs/integrating/nextjs.md +3 -4
  72. package/docs/integrating/svelte.md +5 -4
  73. package/docs/integrating/tailwind.md +16 -3
  74. package/docs/integrating/vue.md +3 -4
  75. package/docs/integrating/without-a-bundler.md +114 -0
  76. package/docs/pt-BR/accessibility.md +150 -0
  77. package/docs/pt-BR/concepts.md +212 -0
  78. package/docs/pt-BR/index.md +104 -0
  79. package/docs/pt-BR/integrating/astro.md +35 -0
  80. package/docs/pt-BR/integrating/index.md +47 -0
  81. package/docs/pt-BR/integrating/nextjs.md +67 -0
  82. package/docs/pt-BR/integrating/svelte.md +45 -0
  83. package/docs/pt-BR/integrating/tailwind.md +78 -0
  84. package/docs/pt-BR/integrating/vue.md +31 -0
  85. package/docs/pt-BR/integrating/without-a-bundler.md +115 -0
  86. package/docs/pt-BR/setup.md +180 -0
  87. package/docs/pt-BR/usage/aesthetics.md +276 -0
  88. package/docs/pt-BR/usage/buttons.md +131 -0
  89. package/docs/pt-BR/usage/composing.md +164 -0
  90. package/docs/pt-BR/usage/content-and-layout.md +215 -0
  91. package/docs/pt-BR/usage/customizing.md +298 -0
  92. package/docs/pt-BR/usage/feedback.md +153 -0
  93. package/docs/pt-BR/usage/forms.md +231 -0
  94. package/docs/pt-BR/usage/index.md +29 -0
  95. package/docs/pt-BR/usage/solo.md +84 -0
  96. package/docs/pt-BR/usage/theming.md +296 -0
  97. package/docs/pt-BR/usage/tooltips.md +127 -0
  98. package/docs/setup.md +134 -91
  99. package/docs/usage/aesthetics.md +76 -66
  100. package/docs/usage/buttons.md +108 -40
  101. package/docs/usage/composing.md +105 -92
  102. package/docs/usage/content-and-layout.md +157 -126
  103. package/docs/usage/customizing.md +107 -37
  104. package/docs/usage/feedback.md +134 -71
  105. package/docs/usage/forms.md +176 -107
  106. package/docs/usage/index.md +18 -14
  107. package/docs/usage/solo.md +83 -0
  108. package/docs/usage/theming.md +260 -118
  109. package/docs/usage/tooltips.md +90 -49
  110. package/llms-full.txt +2358 -1313
  111. package/llms.txt +24 -22
  112. package/package.json +25 -32
  113. package/dist/tw/components/loader.css +0 -116
@@ -1,77 +1,101 @@
1
1
  ---
2
2
  title: Tooltips
3
- description: Tooltip composition classes and positioning attributes.
4
- order: 8
3
+ description: Field help that opens on hover and focus, how to connect it for screen readers and Escape, and where a tooltip gets cut off.
4
+ order: 4
5
5
  ---
6
6
 
7
7
  # Tooltips
8
8
 
9
- | Class or Attribute | Purpose |
10
- | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
11
- | `.tooltip` | Wrapper around a trigger and a bubble. Paints nothing itself. |
12
- | `.tooltip-bubble` | The message. A real element, styled like any other surface. |
13
- | `.tooltip-icon` | Round icon-style trigger, for a message with nothing else to hang on. It takes the aesthetic's corner: square under `.neobrutalism` and `.pixel`, a diamond under `.cyber`. |
14
- | `[data-tooltip-position="top"]` | Positions the bubble above the trigger, on `.tooltip`. |
15
- | `[data-tooltip-position="bottom"]` | Positions the bubble below the trigger, on `.tooltip`. |
16
- | `[data-tooltip-position="left"]` | Positions the bubble left of the trigger, on `.tooltip`. |
17
- | `[data-tooltip-position="right"]` | Positions the bubble right of the trigger, on `.tooltip`. |
18
- | No `data-tooltip-position` | Defaults to top placement. |
19
- | `[data-state="open"]` | Shows the bubble without hover, on `.tooltip`. |
20
- | `[data-state="closed"]` | Hides the bubble even while hovered or focused, on `.tooltip`. For an Escape handler. |
21
- | `.tooltip-bubble.raised` / `.floating` | Adds one or two units of depth. The bubble is flat by default. |
9
+ The "Public profile" switch on the settings screen deserves a short explanation: what becomes visible when it is on. This page adds a help button beside it that shows a tooltip on hover and on keyboard focus. The CSS shows and hides the message and draws it; you connect it to its trigger for screen readers, add the Escape key, and check that nothing around it cuts it off.
22
10
 
23
- A tooltip is composition, not a single class: `.tooltip` wraps a trigger element and a `.tooltip-bubble`, and shows the bubble on hover or focus within the wrapper. The bubble is a real element because CSS generated content is not reliably exposed to assistive tech and cannot be the target of an `aria-describedby`, which needs a real element with a real `id`. Wire the association yourself: give the bubble an `id`, give the trigger `aria-describedby` pointing at it, and put `role="tooltip"` on the bubble.
11
+ ## A help tooltip
24
12
 
25
- A tooltip bubble's fill, ground color, and floor behavior are covered in [Composing → Presentation](./composing.md#presentation), and its material follows whichever [aesthetic](./aesthetics.md) is in scope. Intent, presentation, aesthetic, and elevation classes go on `.tooltip-bubble` — the element that actually paints — not on `.tooltip`, the same rule every component in this package follows: put the class on the element that shows it.
13
+ ```html
14
+ <div class="cluster tight">
15
+ <label class="cluster tight">
16
+ <input class="switch primary" type="checkbox" role="switch" name="public-profile" />
17
+ Public profile
18
+ </label>
19
+ <span class="tooltip">
20
+ <button class="tooltip-icon" type="button" aria-label="About public profiles" aria-describedby="public-profile-help">?</button>
21
+ <span class="tooltip-bubble" role="tooltip" id="public-profile-help">Anyone with the link can see your name, photo, and public projects.</span>
22
+ </span>
23
+ </div>
24
+ ```
26
25
 
27
- The bubble rests flat. It draws no shadow of its own on a plain page, and zeroes an aesthetic's part-based depth (such as `.neobrutalism`'s slab) the same way `.flat` does elsewhere. Add [`.raised` or `.floating`](./composing.md#elevation) to lift it, on the bubble itself. An aesthetic whose shadow is a complete value, like `.glass`, still reaches the bubble as it reaches any surface.
26
+ The three parts:
28
27
 
29
- Intent works on a tooltip bubble the same way it works on a filled component: `.tooltip-bubble.primary` fills the bubble with the primary color, black text on a light page and white on a dark one; `.tooltip-bubble.destructive` is a red bubble with the same logic. With no intent, the bubble uses `--color-tooltip`, a plate chosen per theme rather than derived from an intent; see [Composing → Presentation](./composing.md#presentation) for why.
28
+ - `.tooltip` wraps the trigger and the message. It positions the message and paints nothing itself.
29
+ - `.tooltip-bubble` is the message. It is a real element, so it can have an `id` that the trigger points to.
30
+ - `.tooltip-icon` is a small round trigger, for a message with nothing else to attach to. Any focusable element can be the trigger instead: wrap an existing `.btn` in `.tooltip` and it keeps its own look.
30
31
 
31
- ## Example
32
+ Connect them yourself: an `id` on the bubble, `aria-describedby` on the trigger pointing to that `id`, and `role="tooltip"` on the bubble. A screen reader then reads the message as the trigger's description. The CSS cannot make this connection, and without it the message is visual only.
32
33
 
33
- ```html
34
- <span class="tooltip">
35
- <button aria-describedby="status-bubble">Status</button>
36
- <span class="tooltip-bubble" role="tooltip" id="status-bubble">Saved 2 minutes ago</span>
37
- </span>
34
+ The trigger must be focusable, such as a `<button>`, so keyboard users can reach the message. The bubble opens when anything inside `.tooltip` has focus, so it also opens after a mouse click on the trigger.
38
35
 
36
+ ## Placement
37
+
38
+ The bubble opens above its trigger, horizontally centered, with a 0.5rem gap. Set `data-tooltip-position` on `.tooltip` to choose another side:
39
+
40
+ ```html
39
41
  <span class="tooltip" data-tooltip-position="bottom">
40
- <button aria-describedby="docs-bubble">Docs</button>
41
- <span class="tooltip-bubble" role="tooltip" id="docs-bubble">Opens in a new tab</span>
42
+ <button class="btn ghost sm" type="button" aria-describedby="renewal-help">Renews 12 March 2027</button>
43
+ <span class="tooltip-bubble" role="tooltip" id="renewal-help">Your card is charged on this date.</span>
42
44
  </span>
45
+ ```
43
46
 
44
- <span class="tooltip">
45
- <button class="btn primary" aria-describedby="deploy-bubble">Deploy</button>
46
- <span class="tooltip-bubble raised primary" role="tooltip" id="deploy-bubble">Primary intent, lifted a step</span>
47
- </span>
47
+ | `data-tooltip-position` | Bubble |
48
+ | ----------------------- | ------------------------------------------------- |
49
+ | _none_ or `"top"` | Above the trigger, centered. |
50
+ | `"bottom"` | Below the trigger, centered. |
51
+ | `"left"` | To the left of the trigger, centered vertically. |
52
+ | `"right"` | To the right of the trigger, centered vertically. |
48
53
 
49
- <span class="tooltip" data-state="open">
50
- <span aria-describedby="pinned-bubble">Pinned</span>
51
- <span class="tooltip-bubble" role="tooltip" id="pinned-bubble">Always visible, no hover needed</span>
52
- </span>
54
+ A bubble is as wide as its text, up to 18rem, and then wraps.
53
55
 
54
- <span class="tooltip" data-tooltip-position="right">
55
- <button type="button" class="tooltip-icon" aria-label="More details" aria-describedby="icon-bubble">?</button>
56
- <span class="tooltip-bubble" role="tooltip" id="icon-bubble">More details</span>
57
- </span>
58
- ```
56
+ ### Where a tooltip gets cut off
57
+
58
+ The bubble is positioned with CSS only, on the side you chose. Nothing measures the page: it does not move to stay inside the window, flip to the other side when there is no room, or move to the top layer of the page. That has three consequences:
59
+
60
+ - **A scrolling or clipping container cuts it off.** An ancestor with `overflow: hidden`, `auto`, or `scroll` clips the bubble at its edge. The API keys table on the settings screen sits in a `.table-wrap`, which scrolls, so a top tooltip on the first row is cut off at the top of the wrapper. Raising `z-index` does not help: it changes what the bubble is painted above, not whether the container clips it.
61
+ - **The window edge cuts it off.** A top tooltip on the first line of a page, or a right tooltip near the right edge, extends off screen.
62
+ - **Left and right are physical sides.** In a right-to-left page, `"left"` still means the left side of the screen.
59
63
 
60
- The first two show plain triggers with default (top) and explicit bottom placement. The third combines an intent with `.raised` on the bubble to show they compose freely — the intent decides the bubble's fill, `.raised` decides whether it casts depth. The fourth uses `data-state="open"` on the wrapper to keep a tooltip visible without a hover or focus interaction, useful for a tooltip that should be demonstrated or tested without simulating pointer input. The fifth uses `.tooltip-icon` as the trigger, for a message with no other element to hang it on.
64
+ What to do:
61
65
 
62
- `.tooltip` composes with any trigger: wrap an existing `.btn` or `.badge` in it and both keep their own look, gaining a bubble beside them. `.tooltip-icon` is only the round chip for the case where there is nothing else to be the trigger.
66
+ - Choose a side with room: on a table's first row, use `data-tooltip-position="bottom"`.
67
+ - Move the tooltip's trigger out of the clipping container, or remove the `overflow` from a container that does not need it.
68
+ - If the trigger's position cannot be known in advance, such as in a scrolling list or a resizable panel, use a positioning library or your framework's popover component, which can measure and flip. You can still give its message element the `.tooltip-bubble` classes for the look, but the showing, hiding, and positioning described on this page then come from that library, not from `.tooltip`.
63
69
 
64
- Showing and hiding the bubble is CSS-only — `:hover` and `:focus-within` on `.tooltip` — but that is presentational. It is not a complete accessible tooltip implementation by itself: provide the `id`/`aria-describedby`/`role="tooltip"` association outside the CSS; see [Accessibility](../accessibility.md).
70
+ ## Hover, linger, and Escape
65
71
 
66
- The bubble is hoverable, as WCAG 1.4.13 asks of content that appears on hover: once the pointer leaves the trigger, the bubble stays for `--tooltip-linger` (`300ms`, set on `.tooltip`) and then fades, so the pointer can cross the gap onto it, and it stays open for as long as the pointer is over it. A closed bubble is `visibility: hidden`, so it is nothing under the pointer. It stays in the accessibility tree through `aria-describedby`, which reads hidden content, but a bubble with no `aria-describedby` pointing at it is no longer read in place while hidden.
72
+ WCAG 1.4.13 asks three things of content that appears on hover or focus. The package handles two:
67
73
 
68
- 1.4.13 also asks that the message can be dismissed without moving the pointer or focus, which takes a key handler. Write `data-state="closed"` on `.tooltip` on Escape, and clear it when the pointer and focus leave:
74
+ - **Hoverable.** When the pointer leaves the trigger, the bubble stays for `--tooltip-linger` (300ms) and then fades, so the pointer can cross the gap onto it. It stays open while the pointer is over it. A closed bubble is `visibility: hidden`, so it never catches the pointer.
75
+ - **Persistent.** It stays open until the pointer and focus leave.
76
+ - **Dismissible.** The user must be able to close it without moving the pointer or focus, usually with Escape. CSS cannot listen for a key, so this part is yours.
77
+
78
+ To change the linger time, set `--tooltip-linger` on the `.tooltip` element, for example `style="--tooltip-linger: 500ms"`. The wrapper declares its own default, so a value set on a parent of `.tooltip` has no effect. Keep it long enough for the pointer to reach the bubble. It is one of six [public component variables](./customizing.md#component-variables).
79
+
80
+ For Escape, write `data-state="closed"` on `.tooltip`: it hides the bubble even while it is hovered or focused. Remove the attribute only when both the pointer and focus have left the tooltip, so the next hover or focus opens it again. Removing it earlier, while the trigger still has focus, makes the message reappear without the user doing anything.
69
81
 
70
82
  ```js
71
83
  for (const tooltip of document.querySelectorAll(".tooltip")) {
72
- const reopen = () => tooltip.removeAttribute("data-state");
73
- tooltip.addEventListener("pointerleave", reopen);
74
- tooltip.addEventListener("focusout", reopen);
84
+ let pointerInside = tooltip.matches(":hover");
85
+
86
+ tooltip.addEventListener("pointerenter", () => {
87
+ pointerInside = true;
88
+ });
89
+
90
+ tooltip.addEventListener("pointerleave", () => {
91
+ pointerInside = false;
92
+ if (!tooltip.contains(document.activeElement)) tooltip.removeAttribute("data-state");
93
+ });
94
+
95
+ tooltip.addEventListener("focusout", (event) => {
96
+ const focusStaysInside = tooltip.contains(event.relatedTarget);
97
+ if (!focusStaysInside && !pointerInside) tooltip.removeAttribute("data-state");
98
+ });
75
99
  }
76
100
 
77
101
  document.addEventListener("keydown", (event) => {
@@ -82,4 +106,21 @@ document.addEventListener("keydown", (event) => {
82
106
  });
83
107
  ```
84
108
 
85
- Next: [Content and layout](./content-and-layout.md) covers layout helpers, content chips and tables, surfaces, and typography utilities.
109
+ `data-state="open"` does the opposite: it shows the bubble with no hover or focus, for a message that should stay visible, or for a test or a screenshot.
110
+
111
+ ## How the bubble looks
112
+
113
+ With no intent class, the bubble uses `--color-tooltip`, a background chosen per theme: near-white on a light page, mid gray on a dark one, with `--color-tooltip-contrast` as its text color. Near-white measures only 1.04:1 against a light page, so the bubble also draws a thin border, and a container's `.edgeless` cannot remove it. `.edgeless` on the bubble itself still can. The background stays opaque under any presentation, so the bubble stays readable over whatever it covers.
114
+
115
+ An intent class fills the bubble with that intent's color and switches the text to the matching contrast color:
116
+
117
+ - `.tooltip-bubble.primary` has a near-black background with near-white text on a light page, and a near-white background with near-black text on a dark one.
118
+ - `.tooltip-bubble.destructive` has a red background with light text in both themes.
119
+
120
+ Put intent, presentation, aesthetic, and elevation classes on `.tooltip-bubble`, the element that is painted, not on `.tooltip`. The bubble is flat by default, even under an aesthetic that draws depth; add `.raised` or `.floating` to the bubble to lift it. An aesthetic in scope shapes the bubble like any other component; under `.glass` it is frosted like a card. `.tooltip-icon` takes the aesthetic's corner: square under `.neobrutalism` and `.pixel`, a diamond under `.cyber`.
121
+
122
+ The bubble appears without scaling up when the user asks for reduced motion.
123
+
124
+ ## Next
125
+
126
+ The settings screen is complete in its default look. [Theming](./theming.md) gives it a dark mode and your brand colors.