@womp/kookie-ui 0.30.0 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components.css +56 -51
- package/dist/cjs/components/_internal/page-scope.d.ts +48 -0
- package/dist/cjs/components/_internal/page-scope.d.ts.map +1 -0
- package/dist/cjs/components/_internal/page-scope.js +3 -0
- package/dist/cjs/components/_internal/page-scope.js.map +7 -0
- package/dist/cjs/components/_internal/portal-theme.d.ts +2 -1
- package/dist/cjs/components/_internal/portal-theme.d.ts.map +1 -1
- package/dist/cjs/components/_internal/portal-theme.js +1 -1
- package/dist/cjs/components/_internal/portal-theme.js.map +4 -4
- package/dist/cjs/components/_internal/shell-bottom.d.ts.map +1 -1
- package/dist/cjs/components/_internal/shell-bottom.js +1 -1
- package/dist/cjs/components/_internal/shell-bottom.js.map +4 -4
- package/dist/cjs/components/_internal/shell-inspector.d.ts.map +1 -1
- package/dist/cjs/components/_internal/shell-inspector.js +1 -1
- package/dist/cjs/components/_internal/shell-inspector.js.map +4 -4
- package/dist/cjs/components/_internal/shell-sidebar.d.ts.map +1 -1
- package/dist/cjs/components/_internal/shell-sidebar.js +1 -1
- package/dist/cjs/components/_internal/shell-sidebar.js.map +4 -4
- package/dist/cjs/components/alert-dialog.js +1 -1
- package/dist/cjs/components/alert-dialog.js.map +4 -4
- package/dist/cjs/components/button.js +1 -1
- package/dist/cjs/components/button.js.map +4 -4
- package/dist/cjs/components/chatbar.js +1 -1
- package/dist/cjs/components/chatbar.js.map +4 -4
- package/dist/cjs/components/chip.js +1 -1
- package/dist/cjs/components/chip.js.map +4 -4
- package/dist/cjs/components/combobox.js +1 -1
- package/dist/cjs/components/combobox.js.map +4 -4
- package/dist/cjs/components/context-menu.js +1 -1
- package/dist/cjs/components/context-menu.js.map +4 -4
- package/dist/cjs/components/dialog.js +1 -1
- package/dist/cjs/components/dialog.js.map +4 -4
- package/dist/cjs/components/dropdown-menu.js +1 -1
- package/dist/cjs/components/dropdown-menu.js.map +4 -4
- package/dist/cjs/components/index.d.ts +1 -0
- package/dist/cjs/components/index.d.ts.map +1 -1
- package/dist/cjs/components/index.js +2 -2
- package/dist/cjs/components/index.js.map +4 -4
- package/dist/cjs/components/link.js +1 -1
- package/dist/cjs/components/link.js.map +3 -3
- package/dist/cjs/components/link.props.d.ts +0 -6
- package/dist/cjs/components/link.props.d.ts.map +1 -1
- package/dist/cjs/components/link.props.js +1 -1
- package/dist/cjs/components/link.props.js.map +3 -3
- package/dist/cjs/components/message-scroller.js +1 -1
- package/dist/cjs/components/message-scroller.js.map +4 -4
- package/dist/cjs/components/navigator.d.ts +11 -7
- package/dist/cjs/components/navigator.d.ts.map +1 -1
- package/dist/cjs/components/navigator.js +1 -1
- package/dist/cjs/components/navigator.js.map +4 -4
- package/dist/cjs/components/notice.js +1 -1
- package/dist/cjs/components/notice.js.map +4 -4
- package/dist/cjs/components/page.d.ts +31 -0
- package/dist/cjs/components/page.d.ts.map +1 -0
- package/dist/cjs/components/page.js +3 -0
- package/dist/cjs/components/page.js.map +7 -0
- package/dist/cjs/components/popover.js +1 -1
- package/dist/cjs/components/popover.js.map +4 -4
- package/dist/cjs/components/select.js +1 -1
- package/dist/cjs/components/select.js.map +4 -4
- package/dist/cjs/components/sheet.js +1 -1
- package/dist/cjs/components/sheet.js.map +4 -4
- package/dist/cjs/components/shell.d.ts.map +1 -1
- package/dist/cjs/components/shell.js +1 -1
- package/dist/cjs/components/shell.js.map +4 -4
- package/dist/cjs/components/sidebar.js +1 -1
- package/dist/cjs/components/sidebar.js.map +4 -4
- package/dist/cjs/components/toggle.js +1 -1
- package/dist/cjs/components/toggle.js.map +4 -4
- package/dist/cjs/components/toolbar.d.ts +5 -1
- package/dist/cjs/components/toolbar.d.ts.map +1 -1
- package/dist/cjs/components/toolbar.js +1 -1
- package/dist/cjs/components/toolbar.js.map +4 -4
- package/dist/cjs/components/tooltip.js +1 -1
- package/dist/cjs/components/tooltip.js.map +4 -4
- package/dist/cjs/index.js +5 -5
- package/dist/cjs/index.js.map +4 -4
- package/dist/cjs/props/index.js +1 -1
- package/dist/cjs/props/index.js.map +3 -3
- package/dist/esm/{chunk-HBO7W7SS.js → chunk-26O4RBOJ.js} +2 -2
- package/dist/esm/chunk-3H33S3UA.js +3 -0
- package/dist/esm/{chunk-C67MM24U.js.map → chunk-3H33S3UA.js.map} +3 -3
- package/dist/esm/{chunk-FYTLUDL3.js → chunk-3M5ZM67R.js} +2 -2
- package/dist/esm/chunk-4B3S2DM7.js +3 -0
- package/dist/esm/chunk-4B3S2DM7.js.map +7 -0
- package/dist/esm/{chunk-NEKIU24N.js → chunk-4ZHBNYXM.js} +2 -2
- package/dist/esm/{chunk-TSS4K2U5.js → chunk-5VYQDA5S.js} +2 -2
- package/dist/esm/chunk-65URNXF2.js +3 -0
- package/dist/esm/chunk-65URNXF2.js.map +7 -0
- package/dist/esm/chunk-6SGJWML7.js +3 -0
- package/dist/esm/chunk-6SGJWML7.js.map +7 -0
- package/dist/esm/{chunk-R4UKO4UK.js → chunk-6WHKGVWY.js} +2 -2
- package/dist/esm/{chunk-7V6M3MIR.js → chunk-7YXHLU4C.js} +2 -2
- package/dist/esm/chunk-AKMRPGNP.js +3 -0
- package/dist/esm/chunk-AKMRPGNP.js.map +7 -0
- package/dist/esm/{chunk-ENQV6SEA.js → chunk-AKSAKP35.js} +2 -2
- package/dist/esm/{chunk-FRFVGYGX.js → chunk-BQOFHLDP.js} +2 -2
- package/dist/esm/{chunk-ZYQEFENG.js → chunk-COLJWVN7.js} +2 -2
- package/dist/esm/{chunk-K3GDM26A.js → chunk-D42VCLKH.js} +2 -2
- package/dist/esm/{chunk-MGWGD6KI.js → chunk-DFQIIYLD.js} +2 -2
- package/dist/esm/{chunk-NWXODCTD.js → chunk-EERKYRYI.js} +2 -2
- package/dist/esm/{chunk-4MNRLB57.js → chunk-FICBVI4M.js} +2 -2
- package/dist/esm/chunk-HGF4CKRY.js +3 -0
- package/dist/esm/chunk-HGF4CKRY.js.map +7 -0
- package/dist/esm/{chunk-JD4KR6VZ.js → chunk-HVMRB3AH.js} +2 -2
- package/dist/esm/{chunk-HLIOHDEX.js → chunk-IFHBMTFX.js} +2 -2
- package/dist/esm/{chunk-J54GYD7A.js → chunk-JFJV4HL3.js} +2 -2
- package/dist/esm/{chunk-KJZSXLIX.js → chunk-JQMWNI62.js} +2 -2
- package/dist/esm/{chunk-CMHKCUHP.js → chunk-K35JUIUT.js} +2 -2
- package/dist/esm/{chunk-67UEMB4K.js → chunk-KBSH6CBD.js} +2 -2
- package/dist/esm/{chunk-SUQJ7DD4.js → chunk-L7PRLW6A.js} +2 -2
- package/dist/esm/{chunk-D2C2A2ZK.js → chunk-LFAUMDU5.js} +2 -2
- package/dist/esm/{chunk-LABMK6Q7.js → chunk-LG5DPQ46.js} +2 -2
- package/dist/esm/{chunk-YMRN5ESN.js → chunk-NFSW36RG.js} +2 -2
- package/dist/esm/{chunk-6YWHNLSC.js → chunk-NJ7RQYLX.js} +2 -2
- package/dist/esm/{chunk-WD66RYF6.js → chunk-NYIYRILR.js} +2 -2
- package/dist/esm/{chunk-JP36JSNP.js → chunk-OBSZOD5M.js} +2 -2
- package/dist/esm/{chunk-DP7MPGR7.js → chunk-OMPLFDS6.js} +2 -2
- package/dist/esm/{chunk-H2J25HXP.js → chunk-OPYTQ34H.js} +2 -2
- package/dist/esm/{chunk-REORAMUH.js → chunk-QBONJMSE.js} +2 -2
- package/dist/esm/chunk-QDAM2I6O.js +3 -0
- package/dist/esm/chunk-QDAM2I6O.js.map +7 -0
- package/dist/esm/chunk-RSG4XWHU.js +2 -0
- package/dist/esm/{chunk-2ZKEQS76.js → chunk-SGKLJDA3.js} +2 -2
- package/dist/esm/{chunk-676Y7ZUT.js → chunk-TMINGBSQ.js} +2 -2
- package/dist/esm/{chunk-2S7HPQVX.js → chunk-TRJAVWCK.js} +2 -2
- package/dist/esm/{chunk-EQE6QQXM.js → chunk-TUZ665BA.js} +2 -2
- package/dist/esm/{chunk-JKQN6QCP.js → chunk-VBF3XALY.js} +2 -2
- package/dist/esm/chunk-VKUAP54W.js +2 -0
- package/dist/esm/chunk-VKUAP54W.js.map +7 -0
- package/dist/esm/chunk-VWRPVM5K.js +3 -0
- package/dist/esm/chunk-VWRPVM5K.js.map +7 -0
- package/dist/esm/{chunk-B6OFKABP.js → chunk-VYF6KKYM.js} +2 -2
- package/dist/esm/{chunk-6UFIS6B2.js → chunk-W35ZKQMQ.js} +2 -2
- package/dist/esm/{chunk-EPGIYTFK.js → chunk-X7DGYRAY.js} +2 -2
- package/dist/esm/{chunk-X7JHZ3HA.js → chunk-XF6Z5NKE.js} +2 -2
- package/dist/esm/{chunk-K5V53KTJ.js → chunk-Y44DYJA5.js} +2 -2
- package/dist/esm/{chunk-MMDZBPSX.js → chunk-YOTTLPJ3.js} +2 -2
- package/dist/esm/chunk-ZBSGH74F.js +3 -0
- package/dist/esm/chunk-ZBSGH74F.js.map +7 -0
- package/dist/esm/chunk-ZHXEEOGI.js +3 -0
- package/dist/esm/chunk-ZHXEEOGI.js.map +7 -0
- package/dist/esm/{chunk-FMVOB367.js → chunk-ZLGOZKB3.js} +2 -2
- package/dist/esm/components/_internal/base-button.js +1 -1
- package/dist/esm/components/_internal/page-scope.d.ts +48 -0
- package/dist/esm/components/_internal/page-scope.d.ts.map +1 -0
- package/dist/esm/components/_internal/page-scope.js +3 -0
- package/dist/esm/components/_internal/page-scope.js.map +7 -0
- package/dist/esm/components/_internal/pane-chrome.js +1 -1
- package/dist/esm/components/_internal/portal-theme.d.ts +2 -1
- package/dist/esm/components/_internal/portal-theme.d.ts.map +1 -1
- package/dist/esm/components/_internal/portal-theme.js +1 -1
- package/dist/esm/components/_internal/shell-bottom.d.ts.map +1 -1
- package/dist/esm/components/_internal/shell-bottom.js +1 -1
- package/dist/esm/components/_internal/shell-inspector.d.ts.map +1 -1
- package/dist/esm/components/_internal/shell-inspector.js +1 -1
- package/dist/esm/components/_internal/shell-sidebar.d.ts.map +1 -1
- package/dist/esm/components/_internal/shell-sidebar.js +1 -1
- package/dist/esm/components/accordion.js +1 -1
- package/dist/esm/components/alert-dialog.js +1 -1
- package/dist/esm/components/avatar.js +1 -1
- package/dist/esm/components/blockquote.js +1 -1
- package/dist/esm/components/button.js +1 -1
- package/dist/esm/components/card.js +1 -1
- package/dist/esm/components/chatbar.js +1 -1
- package/dist/esm/components/checkbox-group.js +1 -1
- package/dist/esm/components/checkbox.js +1 -1
- package/dist/esm/components/chip.js +1 -1
- package/dist/esm/components/code.js +1 -1
- package/dist/esm/components/combobox.js +1 -1
- package/dist/esm/components/context-menu.js +1 -1
- package/dist/esm/components/dialog.js +1 -1
- package/dist/esm/components/dropdown-menu.js +1 -1
- package/dist/esm/components/heading.js +1 -1
- package/dist/esm/components/image.js +1 -1
- package/dist/esm/components/index.d.ts +1 -0
- package/dist/esm/components/index.d.ts.map +1 -1
- package/dist/esm/components/index.js +1 -1
- package/dist/esm/components/link.js +1 -1
- package/dist/esm/components/link.props.d.ts +0 -6
- package/dist/esm/components/link.props.d.ts.map +1 -1
- package/dist/esm/components/link.props.js +1 -1
- package/dist/esm/components/message-scroller.js +1 -1
- package/dist/esm/components/navbar.js +1 -1
- package/dist/esm/components/navigator.d.ts +11 -7
- package/dist/esm/components/navigator.d.ts.map +1 -1
- package/dist/esm/components/navigator.js +1 -1
- package/dist/esm/components/notice.js +1 -1
- package/dist/esm/components/page.d.ts +31 -0
- package/dist/esm/components/page.d.ts.map +1 -0
- package/dist/esm/components/page.js +3 -0
- package/dist/esm/components/page.js.map +7 -0
- package/dist/esm/components/popover.js +1 -1
- package/dist/esm/components/progress.js +1 -1
- package/dist/esm/components/radio-group.js +1 -1
- package/dist/esm/components/radio.js +1 -1
- package/dist/esm/components/scroll-area.js +1 -1
- package/dist/esm/components/segmented-control.js +1 -1
- package/dist/esm/components/select.js +1 -1
- package/dist/esm/components/separator.js +1 -1
- package/dist/esm/components/sheet.js +1 -1
- package/dist/esm/components/shell.d.ts.map +1 -1
- package/dist/esm/components/shell.js +1 -1
- package/dist/esm/components/sidebar.js +1 -1
- package/dist/esm/components/slider.js +1 -1
- package/dist/esm/components/switch.js +1 -1
- package/dist/esm/components/table.js +1 -1
- package/dist/esm/components/tabs.js +1 -1
- package/dist/esm/components/text-area.js +1 -1
- package/dist/esm/components/text-field.js +1 -1
- package/dist/esm/components/text.js +1 -1
- package/dist/esm/components/toggle.js +1 -1
- package/dist/esm/components/toolbar.d.ts +5 -1
- package/dist/esm/components/toolbar.d.ts.map +1 -1
- package/dist/esm/components/toolbar.js +1 -1
- package/dist/esm/components/tooltip.js +1 -1
- package/dist/esm/hooks/index.js +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/props/index.js +1 -1
- package/package.json +1 -1
- package/src/components/_internal/page-scope.tsx +119 -0
- package/src/components/_internal/portal-theme.tsx +6 -2
- package/src/components/_internal/shell-bottom.tsx +4 -1
- package/src/components/_internal/shell-inspector.tsx +4 -1
- package/src/components/_internal/shell-sidebar.tsx +4 -1
- package/src/components/index.css +1 -0
- package/src/components/index.tsx +1 -0
- package/src/components/link.css +16 -46
- package/src/components/link.props.tsx +0 -3
- package/src/components/navigator.tsx +46 -16
- package/src/components/page.css +38 -0
- package/src/components/page.tsx +93 -0
- package/src/components/shell.tsx +8 -2
- package/src/components/toolbar.css +11 -0
- package/src/components/toolbar.tsx +17 -5
- package/styles.css +56 -51
- package/dist/esm/chunk-5KX3RA3P.js +0 -3
- package/dist/esm/chunk-5KX3RA3P.js.map +0 -7
- package/dist/esm/chunk-AQKSO3SW.js +0 -3
- package/dist/esm/chunk-AQKSO3SW.js.map +0 -7
- package/dist/esm/chunk-C67MM24U.js +0 -3
- package/dist/esm/chunk-D74WY6UO.js +0 -3
- package/dist/esm/chunk-D74WY6UO.js.map +0 -7
- package/dist/esm/chunk-E4KT72IB.js +0 -2
- package/dist/esm/chunk-EFXHBH3A.js +0 -2
- package/dist/esm/chunk-EFXHBH3A.js.map +0 -7
- package/dist/esm/chunk-QRV32INS.js +0 -3
- package/dist/esm/chunk-QRV32INS.js.map +0 -7
- package/dist/esm/chunk-VGJALIJ5.js +0 -3
- package/dist/esm/chunk-VGJALIJ5.js.map +0 -7
- package/dist/esm/chunk-WAE4MLBN.js +0 -3
- package/dist/esm/chunk-WAE4MLBN.js.map +0 -7
- package/dist/esm/chunk-YMUERC2J.js +0 -3
- package/dist/esm/chunk-YMUERC2J.js.map +0 -7
- /package/dist/esm/{chunk-HBO7W7SS.js.map → chunk-26O4RBOJ.js.map} +0 -0
- /package/dist/esm/{chunk-FYTLUDL3.js.map → chunk-3M5ZM67R.js.map} +0 -0
- /package/dist/esm/{chunk-NEKIU24N.js.map → chunk-4ZHBNYXM.js.map} +0 -0
- /package/dist/esm/{chunk-TSS4K2U5.js.map → chunk-5VYQDA5S.js.map} +0 -0
- /package/dist/esm/{chunk-R4UKO4UK.js.map → chunk-6WHKGVWY.js.map} +0 -0
- /package/dist/esm/{chunk-7V6M3MIR.js.map → chunk-7YXHLU4C.js.map} +0 -0
- /package/dist/esm/{chunk-ENQV6SEA.js.map → chunk-AKSAKP35.js.map} +0 -0
- /package/dist/esm/{chunk-FRFVGYGX.js.map → chunk-BQOFHLDP.js.map} +0 -0
- /package/dist/esm/{chunk-ZYQEFENG.js.map → chunk-COLJWVN7.js.map} +0 -0
- /package/dist/esm/{chunk-K3GDM26A.js.map → chunk-D42VCLKH.js.map} +0 -0
- /package/dist/esm/{chunk-MGWGD6KI.js.map → chunk-DFQIIYLD.js.map} +0 -0
- /package/dist/esm/{chunk-NWXODCTD.js.map → chunk-EERKYRYI.js.map} +0 -0
- /package/dist/esm/{chunk-4MNRLB57.js.map → chunk-FICBVI4M.js.map} +0 -0
- /package/dist/esm/{chunk-JD4KR6VZ.js.map → chunk-HVMRB3AH.js.map} +0 -0
- /package/dist/esm/{chunk-HLIOHDEX.js.map → chunk-IFHBMTFX.js.map} +0 -0
- /package/dist/esm/{chunk-J54GYD7A.js.map → chunk-JFJV4HL3.js.map} +0 -0
- /package/dist/esm/{chunk-KJZSXLIX.js.map → chunk-JQMWNI62.js.map} +0 -0
- /package/dist/esm/{chunk-CMHKCUHP.js.map → chunk-K35JUIUT.js.map} +0 -0
- /package/dist/esm/{chunk-67UEMB4K.js.map → chunk-KBSH6CBD.js.map} +0 -0
- /package/dist/esm/{chunk-SUQJ7DD4.js.map → chunk-L7PRLW6A.js.map} +0 -0
- /package/dist/esm/{chunk-D2C2A2ZK.js.map → chunk-LFAUMDU5.js.map} +0 -0
- /package/dist/esm/{chunk-LABMK6Q7.js.map → chunk-LG5DPQ46.js.map} +0 -0
- /package/dist/esm/{chunk-YMRN5ESN.js.map → chunk-NFSW36RG.js.map} +0 -0
- /package/dist/esm/{chunk-6YWHNLSC.js.map → chunk-NJ7RQYLX.js.map} +0 -0
- /package/dist/esm/{chunk-WD66RYF6.js.map → chunk-NYIYRILR.js.map} +0 -0
- /package/dist/esm/{chunk-JP36JSNP.js.map → chunk-OBSZOD5M.js.map} +0 -0
- /package/dist/esm/{chunk-DP7MPGR7.js.map → chunk-OMPLFDS6.js.map} +0 -0
- /package/dist/esm/{chunk-H2J25HXP.js.map → chunk-OPYTQ34H.js.map} +0 -0
- /package/dist/esm/{chunk-REORAMUH.js.map → chunk-QBONJMSE.js.map} +0 -0
- /package/dist/esm/{chunk-E4KT72IB.js.map → chunk-RSG4XWHU.js.map} +0 -0
- /package/dist/esm/{chunk-2ZKEQS76.js.map → chunk-SGKLJDA3.js.map} +0 -0
- /package/dist/esm/{chunk-676Y7ZUT.js.map → chunk-TMINGBSQ.js.map} +0 -0
- /package/dist/esm/{chunk-2S7HPQVX.js.map → chunk-TRJAVWCK.js.map} +0 -0
- /package/dist/esm/{chunk-EQE6QQXM.js.map → chunk-TUZ665BA.js.map} +0 -0
- /package/dist/esm/{chunk-JKQN6QCP.js.map → chunk-VBF3XALY.js.map} +0 -0
- /package/dist/esm/{chunk-B6OFKABP.js.map → chunk-VYF6KKYM.js.map} +0 -0
- /package/dist/esm/{chunk-6UFIS6B2.js.map → chunk-W35ZKQMQ.js.map} +0 -0
- /package/dist/esm/{chunk-EPGIYTFK.js.map → chunk-X7DGYRAY.js.map} +0 -0
- /package/dist/esm/{chunk-X7JHZ3HA.js.map → chunk-XF6Z5NKE.js.map} +0 -0
- /package/dist/esm/{chunk-K5V53KTJ.js.map → chunk-Y44DYJA5.js.map} +0 -0
- /package/dist/esm/{chunk-MMDZBPSX.js.map → chunk-YOTTLPJ3.js.map} +0 -0
- /package/dist/esm/{chunk-FMVOB367.js.map → chunk-ZLGOZKB3.js.map} +0 -0
package/src/components/link.css
CHANGED
|
@@ -12,9 +12,12 @@
|
|
|
12
12
|
&:where(:focus-visible) {
|
|
13
13
|
border-radius: calc(0.07em * var(--radius-factor));
|
|
14
14
|
outline-color: var(--focus-8);
|
|
15
|
-
outline-width:
|
|
15
|
+
outline-width: var(--focus-ring-width);
|
|
16
16
|
outline-style: solid;
|
|
17
|
-
outline-offset:
|
|
17
|
+
outline-offset: var(--focus-ring-offset);
|
|
18
|
+
/* A wrapped link rings each line's fragment whole, not with square ends mid-word. */
|
|
19
|
+
-webkit-box-decoration-break: clone;
|
|
20
|
+
box-decoration-break: clone;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
:where([data-accent-color]:not(.kui-theme)) &:where([data-accent-color='']) {
|
|
@@ -22,23 +25,22 @@
|
|
|
22
25
|
}
|
|
23
26
|
}
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
* *
|
|
29
|
-
***************************************************************************************************/
|
|
30
|
-
|
|
28
|
+
/* THE UNDERLINE IS ALWAYS THERE (from v2, 2026-11-03). Colour alone does not tell everyone which
|
|
29
|
+
words are a link (WCAG 1.4.1), and a hover-only underline cannot be found on a touch screen.
|
|
30
|
+
It rests as a quiet line in the link's own colour and darkens to the ink under the pointer. */
|
|
31
31
|
.kui-Link {
|
|
32
32
|
&:where(:any-link, button) {
|
|
33
|
-
text-decoration-line:
|
|
33
|
+
text-decoration-line: underline;
|
|
34
34
|
text-decoration-style: solid;
|
|
35
35
|
text-decoration-thickness: min(2px, max(1px, 0.05em));
|
|
36
|
-
|
|
37
|
-
text-
|
|
36
|
+
/* Relative to the link's own type: a fixed distance crowds a descender large and floats small. */
|
|
37
|
+
text-underline-offset: 0.2em;
|
|
38
|
+
text-decoration-color: var(--accent-a6);
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
@media (hover: hover) {
|
|
41
|
+
&:where(:hover) {
|
|
42
|
+
text-decoration-color: currentcolor;
|
|
43
|
+
}
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
@media (pointer: coarse) {
|
|
@@ -48,41 +50,9 @@
|
|
|
48
50
|
outline-offset: -0.6em;
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
|
-
|
|
52
|
-
&:where(.kui-underline-auto) {
|
|
53
|
-
@media (hover: hover) {
|
|
54
|
-
&:where(:hover) {
|
|
55
|
-
text-decoration-line: underline;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
:where([data-accent-color]:not(.kui-theme)) &:where([data-accent-color='']) {
|
|
60
|
-
text-decoration-line: underline;
|
|
61
|
-
text-decoration-color: var(--accent-a6);
|
|
62
|
-
|
|
63
|
-
/* Enhancement for bright colors */
|
|
64
|
-
@supports (color: color-mix(in oklab, white, black)) {
|
|
65
|
-
text-decoration-color: color-mix(in oklab, var(--accent-a6), var(--neutral-a6));
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
&:where(.kui-underline-hover) {
|
|
71
|
-
@media (hover: hover) {
|
|
72
|
-
&:where(:hover) {
|
|
73
|
-
text-decoration-line: underline;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
&:where(.kui-underline-always) {
|
|
79
|
-
text-decoration-line: underline;
|
|
80
|
-
}
|
|
81
53
|
}
|
|
82
54
|
}
|
|
83
55
|
|
|
84
|
-
/* Focus keeps the link's own underline setting; the focus outline marks focus. */
|
|
85
|
-
|
|
86
56
|
/* Enhancement – hide underline for when Link's only child is a Code where underline is hard to see */
|
|
87
57
|
.kui-Link:where(:has(.kui-Code:not(.kui-variant-ghost):only-child)) {
|
|
88
58
|
text-decoration-color: transparent;
|
|
@@ -8,7 +8,6 @@ import { weightPropDef } from '../props/weight.prop.js';
|
|
|
8
8
|
import type { PropDef } from '../props/prop-def.js';
|
|
9
9
|
|
|
10
10
|
const sizes = ['1', '2', '3', '4', '5', '6', '7', '8', '9'] as const;
|
|
11
|
-
const underline = ['auto', 'always', 'hover', 'none'] as const;
|
|
12
11
|
|
|
13
12
|
const linkPropDefs = {
|
|
14
13
|
...asChildPropDef,
|
|
@@ -22,11 +21,9 @@ const linkPropDefs = {
|
|
|
22
21
|
...leadingTrimPropDef,
|
|
23
22
|
...truncatePropDef,
|
|
24
23
|
...textWrapPropDef,
|
|
25
|
-
underline: { type: 'enum', className: 'kui-underline', values: underline, default: 'auto' },
|
|
26
24
|
...accentColorPropDef,
|
|
27
25
|
} satisfies {
|
|
28
26
|
size: PropDef<(typeof sizes)[number]>;
|
|
29
|
-
underline: PropDef<(typeof underline)[number]>;
|
|
30
27
|
};
|
|
31
28
|
|
|
32
29
|
export { linkPropDefs };
|
|
@@ -8,18 +8,19 @@
|
|
|
8
8
|
* turns a value into a screen, so an app can keep the path in its URL, restore it, or open three
|
|
9
9
|
* levels deep from a link.
|
|
10
10
|
*
|
|
11
|
-
* IT HAS NO REGION OF ITS OWN. The host decides how a screen carries its title. In
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* each screen draws its own band: Back, the
|
|
11
|
+
* IT HAS NO REGION OF ITS OWN. The host decides how a screen carries its title. In
|
|
12
|
+
* `Shell.Content` a screen is a `Page`, whose large title collapses into the band with Back. In a
|
|
13
|
+
* side pane (Sidebar, Panel, Inspector, Bottom) the band holds Back and the title from the start.
|
|
14
|
+
* Anywhere else (a card, a Dialog, a Sheet, a Popover) each screen draws its own band: Back, the
|
|
15
|
+
* title, and the overlay's close when it has one.
|
|
15
16
|
*
|
|
16
17
|
* SCREENS UNDER THE TOP STAY MOUNTED, hidden, so a list keeps its scroll and a field its draft.
|
|
17
18
|
*
|
|
18
19
|
* Focus: a pushed screen takes focus at its top; on pop, focus returns to the control that opened
|
|
19
20
|
* the screen. Escape never goes back; it belongs to whatever overlay holds this.
|
|
20
21
|
*
|
|
21
|
-
* Differs from v2 in look only: no motion, since v1 has none. v1 has no
|
|
22
|
-
*
|
|
22
|
+
* Differs from v2 in look only: no motion, since v1 has none. v1 has no Row yet, so
|
|
23
|
+
* `NavigatorLink` is its own row.
|
|
23
24
|
*/
|
|
24
25
|
|
|
25
26
|
import * as React from 'react';
|
|
@@ -27,9 +28,10 @@ import classNames from 'classnames';
|
|
|
27
28
|
|
|
28
29
|
import { NavigatorHostContext } from './_internal/navigator-host.js';
|
|
29
30
|
import { PaneHeader, Scroll, ShellContentContext } from './_internal/pane-chrome.js';
|
|
30
|
-
import {
|
|
31
|
+
import { setPageCollapsed, useClaimPage, usePageScope } from './_internal/page-scope.js';
|
|
31
32
|
import { Flex } from './flex.js';
|
|
32
33
|
import { Heading } from './heading.js';
|
|
34
|
+
import { Page } from './page.js';
|
|
33
35
|
import { Toolbar, ToolbarButton, ToolbarTitle } from './toolbar.js';
|
|
34
36
|
|
|
35
37
|
type NavigatorApi<T> = {
|
|
@@ -60,14 +62,13 @@ const LevelContext = React.createContext<number>(0);
|
|
|
60
62
|
const OutletContext = React.createContext<React.ReactNode>(null);
|
|
61
63
|
|
|
62
64
|
/**
|
|
63
|
-
* True in a Shell pane,
|
|
64
|
-
* the pane's band carries the title. Overlays
|
|
65
|
+
* True in a Shell pane, `Shell.Content` included, and not in an overlay opened from one: there
|
|
66
|
+
* the pane's band carries Back and the title. Overlays cut the pane's page scope at the portal.
|
|
65
67
|
*/
|
|
66
68
|
function useInPane() {
|
|
67
|
-
const
|
|
68
|
-
const inContent = React.useContext(ShellContentContext);
|
|
69
|
+
const scope = usePageScope();
|
|
69
70
|
const host = React.useContext(NavigatorHostContext);
|
|
70
|
-
return
|
|
71
|
+
return scope !== null && host === null;
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
// Activity keeps a hidden screen's state and cleans up its effects (React 19.2). On an older
|
|
@@ -214,7 +215,7 @@ function PaneFrame() {
|
|
|
214
215
|
{/* One cluster: as two loose toolbar children, the band splits them to its two ends. */}
|
|
215
216
|
<Flex align="center">
|
|
216
217
|
<NavigatorBack />
|
|
217
|
-
<
|
|
218
|
+
<ToolbarTitle />
|
|
218
219
|
</Flex>
|
|
219
220
|
</Toolbar>
|
|
220
221
|
</PaneHeader>
|
|
@@ -282,9 +283,13 @@ function NavigatorBack(props: NavigatorBackProps) {
|
|
|
282
283
|
);
|
|
283
284
|
}
|
|
284
285
|
|
|
285
|
-
/**
|
|
286
|
+
/**
|
|
287
|
+
* The top screen's title, for a toolbar in a frame of your own. In a pane it is the band's
|
|
288
|
+
* mirroring `Toolbar.Title`, which shows once the page's large title has scrolled away.
|
|
289
|
+
*/
|
|
286
290
|
function NavigatorTitle() {
|
|
287
291
|
const state = useNavigatorState();
|
|
292
|
+
if (state.inPane) return <ToolbarTitle />;
|
|
288
293
|
return <ToolbarTitle>{state.titles.current[state.path.length]}</ToolbarTitle>;
|
|
289
294
|
}
|
|
290
295
|
|
|
@@ -305,6 +310,7 @@ function NavigatorScreen({ title, children, className }: NavigatorScreenProps) {
|
|
|
305
310
|
const state = useNavigatorState();
|
|
306
311
|
const depth = React.useContext(LevelContext);
|
|
307
312
|
const Host = React.useContext(NavigatorHostContext);
|
|
313
|
+
const inWorkArea = React.useContext(ShellContentContext);
|
|
308
314
|
const node = React.useRef<HTMLDivElement>(null);
|
|
309
315
|
|
|
310
316
|
React.useEffect(() => {
|
|
@@ -315,7 +321,7 @@ function NavigatorScreen({ title, children, className }: NavigatorScreenProps) {
|
|
|
315
321
|
// pane the title is in the band, outside the screen, so the screen itself takes it.
|
|
316
322
|
React.useEffect(() => {
|
|
317
323
|
if (depth === 0 || state.direction !== 'push') return;
|
|
318
|
-
const target = node.current?.querySelector<HTMLElement>('.kui-NavigatorBar .kui-ToolbarTitle') ?? node.current;
|
|
324
|
+
const target = node.current?.querySelector<HTMLElement>('.kui-NavigatorBar .kui-ToolbarTitle, h1') ?? node.current;
|
|
319
325
|
if (target) {
|
|
320
326
|
target.tabIndex = -1;
|
|
321
327
|
target.focus({ preventScroll: true });
|
|
@@ -324,14 +330,27 @@ function NavigatorScreen({ title, children, className }: NavigatorScreenProps) {
|
|
|
324
330
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
325
331
|
}, []);
|
|
326
332
|
|
|
333
|
+
if (state.inPane && inWorkArea) {
|
|
334
|
+
return (
|
|
335
|
+
<div ref={node} className={classNames('kui-NavigatorScreen', className)}>
|
|
336
|
+
<Page title={title}>{children}</Page>
|
|
337
|
+
</div>
|
|
338
|
+
);
|
|
339
|
+
}
|
|
327
340
|
if (state.inPane) {
|
|
341
|
+
// A side pane keeps its title in its band: a page's large title would dwarf a narrow
|
|
342
|
+
// inspector's rows. The screen claims the pane's title, collapsed from the start.
|
|
328
343
|
return (
|
|
329
344
|
<div ref={node} className={classNames('kui-NavigatorScreen', className)}>
|
|
345
|
+
<SidePaneTitle title={title} />
|
|
330
346
|
{children}
|
|
331
347
|
</div>
|
|
332
348
|
);
|
|
333
349
|
}
|
|
334
350
|
|
|
351
|
+
// Only the top screen names the panel. Screens underneath stay mounted, and if each wrapped its
|
|
352
|
+
// title in the overlay's title part they would share its id, and the panel would keep the root's name.
|
|
353
|
+
const isTop = depth === state.path.length;
|
|
335
354
|
const heading = (
|
|
336
355
|
<Heading as="h2" size="4" weight="medium" truncate>
|
|
337
356
|
{title}
|
|
@@ -348,7 +367,7 @@ function NavigatorScreen({ title, children, className }: NavigatorScreenProps) {
|
|
|
348
367
|
<BackGlyph />
|
|
349
368
|
</ToolbarButton>
|
|
350
369
|
) : null}
|
|
351
|
-
<ToolbarTitle className="kui-NavigatorBarTitle">{Host ? <Host.Title>{heading}</Host.Title> : heading}</ToolbarTitle>
|
|
370
|
+
<ToolbarTitle className="kui-NavigatorBarTitle">{Host && isTop ? <Host.Title>{heading}</Host.Title> : heading}</ToolbarTitle>
|
|
352
371
|
{Host ? (
|
|
353
372
|
<Host.Close>
|
|
354
373
|
<ToolbarButton iconOnly aria-label="Close">
|
|
@@ -363,6 +382,17 @@ function NavigatorScreen({ title, children, className }: NavigatorScreenProps) {
|
|
|
363
382
|
);
|
|
364
383
|
}
|
|
365
384
|
|
|
385
|
+
function SidePaneTitle({ title }: { title: string }) {
|
|
386
|
+
const store = usePageScope();
|
|
387
|
+
useClaimPage(store, title);
|
|
388
|
+
React.useEffect(() => {
|
|
389
|
+
if (!store) return;
|
|
390
|
+
setPageCollapsed(store, true);
|
|
391
|
+
return () => setPageCollapsed(store, false);
|
|
392
|
+
}, [store]);
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
|
|
366
396
|
interface NavigatorLinkProps<T> extends Omit<React.ComponentPropsWithoutRef<'button'>, 'value' | 'onClick'> {
|
|
367
397
|
/** The value pushed onto the path when the row is pressed. */
|
|
368
398
|
value: T;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* Page (from v2, 2026-11-03): the screen you navigated to. Three distances and a marker; the
|
|
2
|
+
* width is the frame's to state. */
|
|
3
|
+
.kui-Page {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
/* From the title block to whatever the page begins with: two steps clear of the title-to-
|
|
7
|
+
description interval, so a section does not read as a third line of the description. */
|
|
8
|
+
gap: var(--layout-space-8);
|
|
9
|
+
/* Air above the title. A floating band's reach is already cleared by `Shell.Scroll`, which
|
|
10
|
+
pads its content by it, so only the air is left to add. */
|
|
11
|
+
padding-block-start: var(--layout-space-7);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/* The title and its description are one block, and the marker is placed against it. */
|
|
15
|
+
.kui-PageHeader {
|
|
16
|
+
position: relative;
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
gap: var(--layout-space-6);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* The mark and the title are a lockup, so they sit closer than the title and description. */
|
|
23
|
+
.kui-PageMark {
|
|
24
|
+
margin-block-end: calc(var(--layout-space-4) - var(--layout-space-6));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* Where the title stops being visible. The band floats over the scroller, so the marker sits the
|
|
28
|
+
band's reach above the end of the title block, and crosses the scroller's top edge at the
|
|
29
|
+
moment the title passes behind the band. A hairline tall, since a zero-area box is not
|
|
30
|
+
reliably reported as intersecting; out of flow, and it paints nothing. */
|
|
31
|
+
.kui-PageMarker {
|
|
32
|
+
position: absolute;
|
|
33
|
+
inset-block-end: var(--kui-pane-inset-start, 0px);
|
|
34
|
+
inset-inline-start: 0;
|
|
35
|
+
block-size: 1px;
|
|
36
|
+
inline-size: 100%;
|
|
37
|
+
pointer-events: none;
|
|
38
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import classNames from 'classnames';
|
|
5
|
+
|
|
6
|
+
import { setPageCollapsed, useClaimPage, usePageScope } from './_internal/page-scope.js';
|
|
7
|
+
import { usePageScrollMedia } from './_internal/shell-page-scroll.js';
|
|
8
|
+
import { Heading } from './heading.js';
|
|
9
|
+
import { Text } from './text.js';
|
|
10
|
+
|
|
11
|
+
interface PageProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'color' | 'title'> {
|
|
12
|
+
/**
|
|
13
|
+
* The title of the page. It shows large at the top. When it scrolls away, it also shows in
|
|
14
|
+
* the `Toolbar.Title` of the pane's band. It is a string, because it shows in two places.
|
|
15
|
+
*/
|
|
16
|
+
title: string;
|
|
17
|
+
/** A sentence under the title that describes the page. Optional. */
|
|
18
|
+
description?: React.ReactNode;
|
|
19
|
+
/** A logo or mark above the title, such as an app's logo on its home page. Most pages don't need it. */
|
|
20
|
+
mark?: React.ReactNode;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Page, from v2 (2026-11-03): the screen you navigated to, and its title.
|
|
25
|
+
*
|
|
26
|
+
* A page is not a band. The pinned row with the way back and the tools is the pane's
|
|
27
|
+
* (`Shell.PaneHeader`): it stays while you move from page to page. The large title that arrives
|
|
28
|
+
* and leaves with the content is the page's, and it is this.
|
|
29
|
+
*
|
|
30
|
+
* It owns three things: the air above its title (a floating band's reach is already cleared by
|
|
31
|
+
* `Shell.Scroll`), the title and its description at the page's type steps, and the collapse:
|
|
32
|
+
* when the title passes behind the band, every `Toolbar.Title` with no words of its own in this
|
|
33
|
+
* pane shows the page's title.
|
|
34
|
+
*
|
|
35
|
+
* It takes no width (the frame around it states one), no level (a page is the document's one
|
|
36
|
+
* `h1`), no size (an app has one page step) and no actions (they go in the toolbar).
|
|
37
|
+
*/
|
|
38
|
+
const Page = React.forwardRef<HTMLDivElement, PageProps>(({ title, description, mark, className, children, ...props }, forwardedRef) => {
|
|
39
|
+
const store = usePageScope();
|
|
40
|
+
const marker = React.useRef<HTMLDivElement>(null);
|
|
41
|
+
useClaimPage(store, title);
|
|
42
|
+
|
|
43
|
+
// Re-read when a touch window crosses the phone boundary: the scroller changes from the
|
|
44
|
+
// content's viewport to the page, and an observer built for one never fires for the other.
|
|
45
|
+
const pageScrolls = usePageScrollMedia();
|
|
46
|
+
React.useEffect(() => {
|
|
47
|
+
const el = marker.current;
|
|
48
|
+
if (!el || !store) return;
|
|
49
|
+
// The scroller is the viewport around this, if it scrolls; on a phone window Shell the
|
|
50
|
+
// document scrolls and the viewport is `overflow: visible`, so the root is the document.
|
|
51
|
+
const viewport = el.closest<HTMLElement>('.kui-ScrollAreaViewport');
|
|
52
|
+
const scrolls = viewport !== null && !/^(visible|clip)$/.test(getComputedStyle(viewport).overflowY);
|
|
53
|
+
const observer = new IntersectionObserver(
|
|
54
|
+
(entries) => {
|
|
55
|
+
const entry = entries[entries.length - 1];
|
|
56
|
+
if (!entry) return;
|
|
57
|
+
// Gone off the top, not merely gone: a marker below the fold is also out of view.
|
|
58
|
+
const top = entry.rootBounds?.top;
|
|
59
|
+
setPageCollapsed(store, !entry.isIntersecting && (top === undefined || entry.boundingClientRect.top <= top));
|
|
60
|
+
},
|
|
61
|
+
{ root: scrolls ? viewport : null, threshold: 0 },
|
|
62
|
+
);
|
|
63
|
+
observer.observe(el);
|
|
64
|
+
return () => {
|
|
65
|
+
observer.disconnect();
|
|
66
|
+
// A page that unmounts takes its collapse with it, or the next page's band opens collapsed.
|
|
67
|
+
setPageCollapsed(store, false);
|
|
68
|
+
};
|
|
69
|
+
}, [store, pageScrolls]);
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<div {...props} ref={forwardedRef} className={classNames('kui-Page', className)}>
|
|
73
|
+
<div className="kui-PageHeader">
|
|
74
|
+
{mark === undefined || mark === null ? null : <div className="kui-PageMark">{mark}</div>}
|
|
75
|
+
<Heading as="h1" size="9" weight="medium">
|
|
76
|
+
{title}
|
|
77
|
+
</Heading>
|
|
78
|
+
{description === undefined || description === null ? null : (
|
|
79
|
+
<Text as="p" size="4">
|
|
80
|
+
{description}
|
|
81
|
+
</Text>
|
|
82
|
+
)}
|
|
83
|
+
{/* Out of flow, so it displaces nothing and a page with no band behaves the same. */}
|
|
84
|
+
<div ref={marker} className="kui-PageMarker" aria-hidden="true" />
|
|
85
|
+
</div>
|
|
86
|
+
{children}
|
|
87
|
+
</div>
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
Page.displayName = 'Page';
|
|
91
|
+
|
|
92
|
+
export { Page };
|
|
93
|
+
export type { PageProps };
|
package/src/components/shell.tsx
CHANGED
|
@@ -52,6 +52,7 @@ import { useShell, useSidebarMode } from './shell.context.js';
|
|
|
52
52
|
import { useResponsivePresentation } from './shell.hooks.js';
|
|
53
53
|
import { readBreakpoint } from '../hooks/use-breakpoint.js';
|
|
54
54
|
import { useThemeSize } from '../hooks/theme-size.js';
|
|
55
|
+
import { PageScope } from './_internal/page-scope.js';
|
|
55
56
|
import { PaneChromeScope, PaneFooter, PaneHeader, Scroll, ShellContentContext, ShellSizeContext, ShellWindowContext } from './_internal/pane-chrome.js';
|
|
56
57
|
import { onPageScrollMediaChange, pageScrollMatches } from './_internal/shell-page-scroll.js';
|
|
57
58
|
import type { PaneSize, PaneHeaderProps, PaneFooterProps, ScrollProps } from './_internal/pane-chrome.js';
|
|
@@ -784,7 +785,9 @@ const Panel = React.forwardRef<HTMLDivElement, PanelPublicProps>((props, ref) =>
|
|
|
784
785
|
}
|
|
785
786
|
>
|
|
786
787
|
<div className="kui-ShellPanelContent">
|
|
787
|
-
<PaneChromeScope>
|
|
788
|
+
<PaneChromeScope>
|
|
789
|
+
<PageScope>{content}</PageScope>
|
|
790
|
+
</PaneChromeScope>
|
|
788
791
|
</div>
|
|
789
792
|
{handle}
|
|
790
793
|
</div>
|
|
@@ -808,7 +811,10 @@ const Content = React.forwardRef<HTMLElement, ShellContentProps>(({ className, i
|
|
|
808
811
|
useInsetRegistration('content', inset);
|
|
809
812
|
return (
|
|
810
813
|
<main {...props} ref={ref} className={classNames('kui-ShellContent', className)} data-inset={inset || undefined}>
|
|
811
|
-
|
|
814
|
+
{/* Each pane holds its own page store, so a band's title can mirror the page beside it. */}
|
|
815
|
+
<ShellContentContext.Provider value={true}>
|
|
816
|
+
<PageScope>{children}</PageScope>
|
|
817
|
+
</ShellContentContext.Provider>
|
|
812
818
|
</main>
|
|
813
819
|
);
|
|
814
820
|
});
|
|
@@ -158,6 +158,17 @@
|
|
|
158
158
|
flex-shrink: 1;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
/* A mirroring title is the page's own, said again in the band: silent while the page's large
|
|
162
|
+
title is on screen, shown at once when it is not. Paint only, so the clusters beside it never
|
|
163
|
+
move when someone scrolls. */
|
|
164
|
+
.kui-ToolbarTitle:where([data-mirror]) {
|
|
165
|
+
opacity: 0;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.kui-ToolbarTitle:where([data-mirror][data-collapsed]) {
|
|
169
|
+
opacity: 1;
|
|
170
|
+
}
|
|
171
|
+
|
|
161
172
|
/* A title is a different kind of thing from the controls beside it, so it takes more air than
|
|
162
173
|
they take from each other. It reaches inside a cluster, since a band is often written
|
|
163
174
|
<Flex>{back}{title}</Flex>. No air against the row's own ends. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import * as React from 'react';
|
|
4
|
-
import { useLayoutEffect } from 'radix-ui/internal';
|
|
4
|
+
import { useComposedRefs, useLayoutEffect } from 'radix-ui/internal';
|
|
5
5
|
import classNames from 'classnames';
|
|
6
6
|
import { Toolbar as ToolbarPrimitive } from 'radix-ui';
|
|
7
7
|
|
|
@@ -13,6 +13,7 @@ import { extractProps } from '../helpers/extract-props.js';
|
|
|
13
13
|
import { useThemeSize } from '../hooks/theme-size.js';
|
|
14
14
|
import { layoutPropDefs } from '../props/layout.props.js';
|
|
15
15
|
import { usePaneBandSize } from './_internal/pane-chrome.js';
|
|
16
|
+
import { usePageMirror, usePageScope, usePageTitle } from './_internal/page-scope.js';
|
|
16
17
|
|
|
17
18
|
import type { ButtonProps } from './button.js';
|
|
18
19
|
import type { LayoutProps } from '../props/layout.props.js';
|
|
@@ -671,7 +672,11 @@ ToolbarSeparator.displayName = 'Toolbar.Separator';
|
|
|
671
672
|
const TITLE_SIZE = { '1': '2', '2': '3', '3': '4', '4': '5' } as const satisfies Record<ToolbarSize, TextProps['size']>;
|
|
672
673
|
|
|
673
674
|
interface ToolbarTitleProps {
|
|
674
|
-
/**
|
|
675
|
+
/**
|
|
676
|
+
* The title text. With none, the title mirrors the page in this Shell pane: it shows the
|
|
677
|
+
* `Page`'s title once that title has scrolled behind the band. Outside a pane with a page, it
|
|
678
|
+
* renders nothing.
|
|
679
|
+
*/
|
|
675
680
|
children?: React.ReactNode;
|
|
676
681
|
className?: string;
|
|
677
682
|
style?: React.CSSProperties;
|
|
@@ -689,10 +694,16 @@ const ToolbarTitle = React.forwardRef<HTMLSpanElement, ToolbarTitleProps>(
|
|
|
689
694
|
const toolbar = React.useContext(ToolbarContext);
|
|
690
695
|
const themeSize = useThemeSize<ToolbarSize>(undefined, toolbarRootPropDefs.size);
|
|
691
696
|
const size = toolbar?.size ?? themeSize;
|
|
692
|
-
|
|
697
|
+
const store = usePageScope();
|
|
698
|
+
const pageTitle = usePageTitle(store);
|
|
699
|
+
const mirrorRef = usePageMirror(store);
|
|
700
|
+
const mirroring = children === undefined || children === null || children === false;
|
|
701
|
+
const words = mirroring ? pageTitle : children;
|
|
702
|
+
const ref = useComposedRefs(forwardedRef, mirroring ? mirrorRef : undefined);
|
|
703
|
+
if (words === undefined || words === null) return null;
|
|
693
704
|
return (
|
|
694
705
|
<Text
|
|
695
|
-
ref={
|
|
706
|
+
ref={ref}
|
|
696
707
|
as="span"
|
|
697
708
|
size={TITLE_SIZE[size]}
|
|
698
709
|
weight="medium"
|
|
@@ -700,8 +711,9 @@ const ToolbarTitle = React.forwardRef<HTMLSpanElement, ToolbarTitleProps>(
|
|
|
700
711
|
id={id}
|
|
701
712
|
style={style}
|
|
702
713
|
className={classNames('kui-ToolbarTitle', className)}
|
|
714
|
+
data-mirror={mirroring || undefined}
|
|
703
715
|
>
|
|
704
|
-
{
|
|
716
|
+
{words}
|
|
705
717
|
</Text>
|
|
706
718
|
);
|
|
707
719
|
},
|
package/styles.css
CHANGED
|
@@ -12558,19 +12558,19 @@
|
|
|
12558
12558
|
}
|
|
12559
12559
|
.kui-Link:where(:-moz-any-link,button) {
|
|
12560
12560
|
cursor: var(--cursor-link);
|
|
12561
|
-
text-decoration-line:
|
|
12561
|
+
text-decoration-line: underline;
|
|
12562
12562
|
text-decoration-style: solid;
|
|
12563
12563
|
text-decoration-thickness: min(2px, max(1px, 0.05em));
|
|
12564
|
-
text-underline-offset:
|
|
12565
|
-
text-decoration-color: var(--accent-
|
|
12564
|
+
text-underline-offset: 0.2em;
|
|
12565
|
+
text-decoration-color: var(--accent-a6);
|
|
12566
12566
|
}
|
|
12567
12567
|
.kui-Link:where(:any-link,button) {
|
|
12568
12568
|
cursor: var(--cursor-link);
|
|
12569
|
-
text-decoration-line:
|
|
12569
|
+
text-decoration-line: underline;
|
|
12570
12570
|
text-decoration-style: solid;
|
|
12571
12571
|
text-decoration-thickness: min(2px, max(1px, 0.05em));
|
|
12572
|
-
text-underline-offset:
|
|
12573
|
-
text-decoration-color: var(--accent-
|
|
12572
|
+
text-underline-offset: 0.2em;
|
|
12573
|
+
text-decoration-color: var(--accent-a6);
|
|
12574
12574
|
}
|
|
12575
12575
|
.kui-Link:where(:disabled,[data-disabled]) {
|
|
12576
12576
|
cursor: var(--cursor-disabled);
|
|
@@ -12578,19 +12578,21 @@
|
|
|
12578
12578
|
.kui-Link:where(:focus-visible) {
|
|
12579
12579
|
border-radius: calc(0.07em * var(--radius-factor));
|
|
12580
12580
|
outline-color: var(--focus-8);
|
|
12581
|
-
outline-width:
|
|
12581
|
+
outline-width: var(--focus-ring-width);
|
|
12582
12582
|
outline-style: solid;
|
|
12583
|
-
outline-offset:
|
|
12583
|
+
outline-offset: var(--focus-ring-offset);
|
|
12584
|
+
-webkit-box-decoration-break: clone;
|
|
12585
|
+
box-decoration-break: clone;
|
|
12584
12586
|
}
|
|
12585
12587
|
:where([data-accent-color]:not(.kui-theme)) .kui-Link:where([data-accent-color='']) {
|
|
12586
12588
|
color: var(--accent-ink);
|
|
12587
12589
|
}
|
|
12588
|
-
@
|
|
12589
|
-
.kui-Link:where(:-moz-any-link,button) {
|
|
12590
|
-
text-decoration-color:
|
|
12590
|
+
@media (hover: hover) {
|
|
12591
|
+
.kui-Link:where(:-moz-any-link,button):where(:hover) {
|
|
12592
|
+
text-decoration-color: currentcolor;
|
|
12591
12593
|
}
|
|
12592
|
-
.kui-Link:where(:any-link,button) {
|
|
12593
|
-
text-decoration-color:
|
|
12594
|
+
.kui-Link:where(:any-link,button):where(:hover) {
|
|
12595
|
+
text-decoration-color: currentcolor;
|
|
12594
12596
|
}
|
|
12595
12597
|
}
|
|
12596
12598
|
@media (pointer: coarse) {
|
|
@@ -12603,44 +12605,6 @@
|
|
|
12603
12605
|
outline-offset: -0.6em;
|
|
12604
12606
|
}
|
|
12605
12607
|
}
|
|
12606
|
-
@media (hover: hover) {
|
|
12607
|
-
.kui-Link:where(:-moz-any-link,button):where(.kui-underline-auto):where(:hover) {
|
|
12608
|
-
text-decoration-line: underline;
|
|
12609
|
-
}
|
|
12610
|
-
.kui-Link:where(:any-link,button):where(.kui-underline-auto):where(:hover) {
|
|
12611
|
-
text-decoration-line: underline;
|
|
12612
|
-
}
|
|
12613
|
-
}
|
|
12614
|
-
:where([data-accent-color]:not(.kui-theme)) .kui-Link:where(:-moz-any-link,button):where(.kui-underline-auto):where([data-accent-color='']) {
|
|
12615
|
-
text-decoration-line: underline;
|
|
12616
|
-
text-decoration-color: var(--accent-a6);
|
|
12617
|
-
}
|
|
12618
|
-
:where([data-accent-color]:not(.kui-theme)) .kui-Link:where(:any-link,button):where(.kui-underline-auto):where([data-accent-color='']) {
|
|
12619
|
-
text-decoration-line: underline;
|
|
12620
|
-
text-decoration-color: var(--accent-a6);
|
|
12621
|
-
}
|
|
12622
|
-
@supports (color: color-mix(in oklab, white, black)) {
|
|
12623
|
-
:where([data-accent-color]:not(.kui-theme)) .kui-Link:where(:-moz-any-link,button):where(.kui-underline-auto):where([data-accent-color='']) {
|
|
12624
|
-
text-decoration-color: color-mix(in oklab, var(--accent-a6), var(--neutral-a6));
|
|
12625
|
-
}
|
|
12626
|
-
:where([data-accent-color]:not(.kui-theme)) .kui-Link:where(:any-link,button):where(.kui-underline-auto):where([data-accent-color='']) {
|
|
12627
|
-
text-decoration-color: color-mix(in oklab, var(--accent-a6), var(--neutral-a6));
|
|
12628
|
-
}
|
|
12629
|
-
}
|
|
12630
|
-
@media (hover: hover) {
|
|
12631
|
-
.kui-Link:where(:-moz-any-link,button):where(.kui-underline-hover):where(:hover) {
|
|
12632
|
-
text-decoration-line: underline;
|
|
12633
|
-
}
|
|
12634
|
-
.kui-Link:where(:any-link,button):where(.kui-underline-hover):where(:hover) {
|
|
12635
|
-
text-decoration-line: underline;
|
|
12636
|
-
}
|
|
12637
|
-
}
|
|
12638
|
-
.kui-Link:where(:-moz-any-link,button):where(.kui-underline-always) {
|
|
12639
|
-
text-decoration-line: underline;
|
|
12640
|
-
}
|
|
12641
|
-
.kui-Link:where(:any-link,button):where(.kui-underline-always) {
|
|
12642
|
-
text-decoration-line: underline;
|
|
12643
|
-
}
|
|
12644
12608
|
.kui-Link:where(:has(.kui-Code:not(.kui-variant-ghost):only-child)) {
|
|
12645
12609
|
text-decoration-color: transparent;
|
|
12646
12610
|
}
|
|
@@ -20480,6 +20444,12 @@
|
|
|
20480
20444
|
-ms-flex-negative: 1;
|
|
20481
20445
|
flex-shrink: 1;
|
|
20482
20446
|
}
|
|
20447
|
+
.kui-ToolbarTitle:where([data-mirror]) {
|
|
20448
|
+
opacity: 0;
|
|
20449
|
+
}
|
|
20450
|
+
.kui-ToolbarTitle:where([data-mirror][data-collapsed]) {
|
|
20451
|
+
opacity: 1;
|
|
20452
|
+
}
|
|
20483
20453
|
:where(.kui-Toolbar) .kui-ToolbarTitle:where(:not(:first-child)) {
|
|
20484
20454
|
-webkit-margin-start: calc(var(--toolbar-title-gap) - var(--toolbar-gap));
|
|
20485
20455
|
margin-inline-start: calc(var(--toolbar-title-gap) - var(--toolbar-gap));
|
|
@@ -20582,6 +20552,41 @@
|
|
|
20582
20552
|
outline: 2px solid var(--focus-8);
|
|
20583
20553
|
outline-offset: -2px;
|
|
20584
20554
|
}
|
|
20555
|
+
.kui-Page {
|
|
20556
|
+
display: -webkit-box;
|
|
20557
|
+
display: -ms-flexbox;
|
|
20558
|
+
display: flex;
|
|
20559
|
+
-webkit-box-orient: vertical;
|
|
20560
|
+
-webkit-box-direction: normal;
|
|
20561
|
+
-ms-flex-direction: column;
|
|
20562
|
+
flex-direction: column;
|
|
20563
|
+
gap: var(--layout-space-8);
|
|
20564
|
+
-webkit-padding-before: var(--layout-space-7);
|
|
20565
|
+
padding-block-start: var(--layout-space-7);
|
|
20566
|
+
}
|
|
20567
|
+
.kui-PageHeader {
|
|
20568
|
+
position: relative;
|
|
20569
|
+
display: -webkit-box;
|
|
20570
|
+
display: -ms-flexbox;
|
|
20571
|
+
display: flex;
|
|
20572
|
+
-webkit-box-orient: vertical;
|
|
20573
|
+
-webkit-box-direction: normal;
|
|
20574
|
+
-ms-flex-direction: column;
|
|
20575
|
+
flex-direction: column;
|
|
20576
|
+
gap: var(--layout-space-6);
|
|
20577
|
+
}
|
|
20578
|
+
.kui-PageMark {
|
|
20579
|
+
-webkit-margin-after: calc(var(--layout-space-4) - var(--layout-space-6));
|
|
20580
|
+
margin-block-end: calc(var(--layout-space-4) - var(--layout-space-6));
|
|
20581
|
+
}
|
|
20582
|
+
.kui-PageMarker {
|
|
20583
|
+
position: absolute;
|
|
20584
|
+
inset-block-end: var(--kui-pane-inset-start, 0px);
|
|
20585
|
+
inset-inline-start: 0;
|
|
20586
|
+
block-size: 1px;
|
|
20587
|
+
inline-size: 100%;
|
|
20588
|
+
pointer-events: none;
|
|
20589
|
+
}
|
|
20585
20590
|
.kui-TooltipContent {
|
|
20586
20591
|
-webkit-box-sizing: border-box;
|
|
20587
20592
|
box-sizing: border-box;
|