astralkit 0.5.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.
@@ -196,12 +196,41 @@ html {
196
196
  .ak-switch-track-icon:last-child { right: 10px; }
197
197
  /* Off state: left icon hidden (behind knob), right icon visible */
198
198
  .ak-switch-with-icons .ak-switch-track-icon:first-child { opacity: 0; }
199
- .ak-switch-with-icons .ak-switch-track-icon:last-child { color: var(--color-ak-on-primary); opacity: 0.7; }
199
+ .ak-switch-with-icons .ak-switch-track-icon:last-child { color: var(--color-ak-inverse-on-surface, #ffffff); opacity: 0.7; }
200
200
  /* On state: left icon visible, right icon hidden (behind knob) */
201
201
  .ak-switch-with-icons[aria-checked="true"] .ak-switch-track-icon:first-child { color: var(--color-ak-on-primary); opacity: 0.7; }
202
202
  .ak-switch-with-icons[aria-checked="true"] .ak-switch-track-icon:last-child { opacity: 0; }
203
+ /* Off-state surfaces: ink track from the inverse pair — palettes tint it, and
204
+ it stays softer than the checked primary track — with the knob swapping the
205
+ same pair so the state icon is legible on every palette in both modes. */
206
+ .ak-switch-with-icons[aria-checked="false"] .ak-switch-track {
207
+ background: color-mix(in srgb, var(--color-ak-inverse-surface, #171717) 84%, var(--color-ak-surface, #ffffff));
208
+ }
209
+ .ak-switch-with-icons[aria-checked="false"] .ak-switch-knob {
210
+ background: var(--color-ak-inverse-on-surface, #ffffff);
211
+ }
203
212
  /* Knob icon */
204
213
  .ak-switch-knob-icon { font-size: 14px; color: var(--color-ak-text); line-height: 1; }
214
+ .ak-switch-with-icons .ak-switch-knob-icon { color: var(--color-ak-inverse-surface, #171717); }
215
+ .ak-switch-with-icons[aria-checked="true"] .ak-switch-knob-icon { color: var(--color-ak-primary); }
216
+ /* Dark mode: the page is already ink — the off track returns to the neutral
217
+ border tone and the inverse pair flips (inverse-surface reads light). */
218
+ .dark .ak-switch-with-icons[aria-checked="false"] .ak-switch-track,
219
+ [data-theme="dark"] .ak-switch-with-icons[aria-checked="false"] .ak-switch-track {
220
+ background: var(--color-ak-border-strong);
221
+ }
222
+ .dark .ak-switch-with-icons[aria-checked="false"] .ak-switch-knob,
223
+ [data-theme="dark"] .ak-switch-with-icons[aria-checked="false"] .ak-switch-knob {
224
+ background: var(--color-ak-inverse-surface, var(--color-ak-text, #ffffff));
225
+ }
226
+ .dark .ak-switch-with-icons .ak-switch-track-icon:last-child,
227
+ [data-theme="dark"] .ak-switch-with-icons .ak-switch-track-icon:last-child {
228
+ color: var(--color-ak-text, #ffffff);
229
+ }
230
+ .dark .ak-switch-with-icons[aria-checked="false"] .ak-switch-knob-icon,
231
+ [data-theme="dark"] .ak-switch-with-icons[aria-checked="false"] .ak-switch-knob-icon {
232
+ color: var(--color-ak-inverse-on-surface, var(--color-ak-bg, #171717));
233
+ }
205
234
 
206
235
  /* Icon switch — lg (88×44) */
207
236
  .ak-switch-icons-lg .ak-switch-track { width: 88px; height: 44px; }
@@ -524,8 +553,258 @@ body.ak-ew-resizing, body.ak-ew-resizing * {
524
553
  cursor: ew-resize !important; user-select: none !important; -webkit-user-select: none !important;
525
554
  }
526
555
 
556
+ /* ── Theme Role Utilities ── */
557
+ .ak-theme-control {
558
+ background: var(--color-ak-surface-container-lowest, var(--color-ak-bg));
559
+ border-color: var(--color-ak-outline-variant, var(--color-ak-border-subtle));
560
+ color: var(--color-ak-on-surface, var(--color-ak-text));
561
+ }
562
+ .ak-theme-control:is(button, a, [role="button"], [tabindex]):not([aria-disabled="true"]):not(:disabled):hover {
563
+ background: var(--color-ak-surface-container-low, var(--color-ak-hover));
564
+ border-color: var(--color-ak-outline, var(--color-ak-border));
565
+ }
566
+ .ak-theme-control-selected {
567
+ background: var(--color-ak-secondary, var(--color-ak-primary));
568
+ border-color: var(--color-ak-secondary, var(--color-ak-primary));
569
+ color: var(--color-ak-on-secondary, var(--color-ak-on-primary));
570
+ }
571
+ .ak-theme-control-selected-soft {
572
+ background: var(--color-ak-secondary-container, var(--color-ak-primary-subtle));
573
+ border-color: var(--color-ak-secondary-outline, var(--color-ak-secondary, var(--color-ak-primary)));
574
+ color: var(--color-ak-on-secondary-container, var(--color-ak-text));
575
+ }
576
+ .ak-theme-control-selected-primary {
577
+ background: var(--color-ak-primary);
578
+ border-color: var(--color-ak-primary);
579
+ color: var(--color-ak-on-primary);
580
+ }
581
+ .ak-theme-control-selected-primary-soft {
582
+ background: var(--color-ak-primary-container, var(--color-ak-primary-subtle));
583
+ border-color: var(--color-ak-primary-outline, var(--color-ak-primary));
584
+ color: var(--color-ak-selection-ink, var(--color-ak-on-primary-container, var(--color-ak-text)));
585
+ }
586
+ .ak-theme-icon-well {
587
+ background: var(--color-ak-surface-container, var(--color-ak-surface-2));
588
+ border-color: var(--color-ak-outline-variant, var(--color-ak-border-subtle));
589
+ color: var(--color-ak-on-surface-variant, var(--color-ak-text-secondary));
590
+ }
591
+ .ak-theme-icon-well-selected {
592
+ background: var(--color-ak-secondary, var(--color-ak-primary));
593
+ border-color: var(--color-ak-secondary, var(--color-ak-primary));
594
+ color: var(--color-ak-on-secondary, var(--color-ak-on-primary));
595
+ }
596
+ .ak-theme-icon-well-selected-primary {
597
+ background: var(--color-ak-primary-selection, var(--color-ak-primary));
598
+ border-color: var(--color-ak-primary-selection, var(--color-ak-primary));
599
+ color: var(--color-ak-on-primary-selection, var(--color-ak-on-primary));
600
+ }
601
+ .ak-theme-badge {
602
+ background: var(--color-ak-surface-container-lowest, var(--color-ak-bg));
603
+ border-color: var(--color-ak-outline-variant, var(--color-ak-border-subtle));
604
+ color: var(--color-ak-on-surface-variant, var(--color-ak-text-secondary));
605
+ }
606
+ .ak-theme-badge-selected {
607
+ background: var(--color-ak-accent, var(--color-ak-primary));
608
+ border-color: var(--color-ak-accent, var(--color-ak-primary));
609
+ color: var(--color-ak-on-accent, var(--color-ak-on-primary));
610
+ }
611
+ .ak-theme-media-caption {
612
+ background: var(--color-ak-primary-container, var(--color-ak-secondary-container, var(--color-ak-surface-container-lowest, var(--color-ak-bg))));
613
+ border-color: color-mix(in srgb, var(--color-ak-primary, var(--color-ak-border)) 34%, transparent);
614
+ color: var(--color-ak-on-primary-container, var(--color-ak-on-secondary-container, var(--color-ak-on-surface, var(--color-ak-text))));
615
+ }
616
+ .ak-theme-cta-gradient {
617
+ background: var(--gradient-ak-primary, linear-gradient(135deg, var(--color-ak-primary-hover), var(--color-ak-primary), var(--color-ak-primary-active)));
618
+ background-size: 160% 160%;
619
+ border-color: transparent;
620
+ color: var(--color-ak-on-primary);
621
+ transition:
622
+ background-position var(--duration-ak-slow, 240ms) var(--ease-ak-default, cubic-bezier(0.4, 0, 0.2, 1)),
623
+ filter var(--duration-ak-fast, 110ms) var(--ease-ak-default, cubic-bezier(0.4, 0, 0.2, 1)),
624
+ transform var(--duration-ak-fast, 110ms) var(--ease-ak-spring, cubic-bezier(0.34, 1.56, 0.64, 1));
625
+ }
626
+ .ak-theme-cta-gradient:is(button, a, [role="button"]):not([aria-disabled="true"]):not(:disabled):hover {
627
+ background: var(--gradient-ak-primary-hover, var(--gradient-ak-primary, linear-gradient(135deg, var(--color-ak-primary-hover), var(--color-ak-primary), var(--color-ak-primary-active))));
628
+ background-position: 100% 50%;
629
+ filter: saturate(1.05);
630
+ }
631
+ .ak-theme-cta-gradient:is(button, a, [role="button"]):not([aria-disabled="true"]):not(:disabled):active {
632
+ transform: translateY(1px) scale(0.99);
633
+ }
634
+ .ak-theme-switch-track {
635
+ background: var(--color-ak-surface-container-high, var(--color-ak-surface-2));
636
+ border-color: var(--color-ak-outline-variant, var(--color-ak-border-subtle));
637
+ box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-ak-on-surface-variant, var(--color-ak-text-secondary)) 6%, transparent);
638
+ }
639
+ .ak-theme-switch-track-selected {
640
+ background: var(--color-ak-secondary, var(--color-ak-primary));
641
+ border-color: var(--color-ak-secondary, var(--color-ak-primary));
642
+ }
643
+ .ak-theme-switch-track-selected-primary {
644
+ background: var(--color-ak-primary);
645
+ border-color: var(--color-ak-primary);
646
+ }
647
+ .ak-theme-switch-thumb {
648
+ background: var(--color-ak-bg);
649
+ box-shadow: var(--shadow-ak-xs);
650
+ }
651
+ .ak-theme-switch-track-selected .ak-theme-switch-thumb {
652
+ background: var(--color-ak-on-secondary, var(--color-ak-on-primary));
653
+ }
654
+ .ak-theme-switch-track-selected-primary .ak-theme-switch-thumb {
655
+ background: var(--color-ak-on-primary);
656
+ }
657
+ .dark .ak-theme-switch-thumb,
658
+ [data-theme="dark"] .ak-theme-switch-thumb,
659
+ [data-ak-dark-panel] .ak-theme-switch-thumb {
660
+ background: var(--color-ak-text);
661
+ }
662
+ .dark .ak-theme-switch-track-selected .ak-theme-switch-thumb,
663
+ [data-theme="dark"] .ak-theme-switch-track-selected .ak-theme-switch-thumb,
664
+ [data-ak-dark-panel] .ak-theme-switch-track-selected .ak-theme-switch-thumb {
665
+ background: var(--color-ak-on-secondary, var(--color-ak-on-primary));
666
+ }
667
+ .dark .ak-theme-switch-track-selected-primary .ak-theme-switch-thumb,
668
+ [data-theme="dark"] .ak-theme-switch-track-selected-primary .ak-theme-switch-thumb,
669
+ [data-ak-dark-panel] .ak-theme-switch-track-selected-primary .ak-theme-switch-thumb {
670
+ background: var(--color-ak-on-primary);
671
+ }
672
+ .ak-theme-logo-mark {
673
+ transition: filter var(--duration-ak-fast, 110ms) var(--ease-ak-default, cubic-bezier(0.4, 0, 0.2, 1)), opacity var(--duration-ak-fast, 110ms) var(--ease-ak-default, cubic-bezier(0.4, 0, 0.2, 1));
674
+ }
675
+ .dark .ak-theme-logo-mark,
676
+ [data-theme="dark"] .ak-theme-logo-mark,
677
+ [data-ak-dark-panel] .ak-theme-logo-mark {
678
+ filter: invert(1) brightness(1.08) contrast(0.96);
679
+ }
680
+
681
+ /* Dark premium material utilities.
682
+ These stay dormant in light mode and use the palette glow tokens in dark contexts. */
683
+ .ak-dark-material:is([data-ak-dark-panel], [data-ak-dark-rail], [data-ak-dark-mobile]),
684
+ .ak-dark-material:is(.dark *),
685
+ .dark .ak-dark-material,
686
+ [data-theme="dark"] .ak-dark-material,
687
+ [data-ak-dark-panel] .ak-dark-material,
688
+ [data-ak-dark-rail] .ak-dark-material,
689
+ [data-ak-dark-mobile] .ak-dark-material {
690
+ background:
691
+ linear-gradient(180deg, color-mix(in srgb, var(--color-ak-on-surface, #fff) 5%, transparent), transparent 42%),
692
+ radial-gradient(90% 64% at 50% 0%, color-mix(in srgb, var(--color-ak-glow-primary, #818cf8) 10%, transparent), transparent 72%),
693
+ color-mix(in srgb, var(--color-ak-bg, #111) 90%, var(--color-ak-mesh-base, #050509) 10%);
694
+ border-color: color-mix(in srgb, var(--color-ak-glow-primary, #818cf8) 18%, var(--color-ak-border, rgba(255,255,255,0.12)));
695
+ color: var(--color-ak-on-surface, var(--color-ak-text));
696
+ }
697
+ .ak-dark-control:is(.dark *),
698
+ .dark .ak-dark-control,
699
+ [data-theme="dark"] .ak-dark-control,
700
+ [data-ak-dark-panel] .ak-dark-control,
701
+ [data-ak-dark-rail] .ak-dark-control,
702
+ [data-ak-dark-mobile] .ak-dark-control {
703
+ background-color: transparent;
704
+ border-color: transparent;
705
+ }
706
+ .ak-dark-outlined-control:is(.dark *),
707
+ .dark .ak-dark-outlined-control,
708
+ [data-theme="dark"] .ak-dark-outlined-control,
709
+ [data-ak-dark-panel] .ak-dark-outlined-control,
710
+ [data-ak-dark-rail] .ak-dark-outlined-control,
711
+ [data-ak-dark-mobile] .ak-dark-outlined-control {
712
+ background:
713
+ linear-gradient(180deg, color-mix(in srgb, var(--color-ak-on-surface, #fff) 3%, transparent), transparent 58%),
714
+ color-mix(in srgb, var(--color-ak-bg, #111) 86%, var(--color-ak-on-surface, #fff) 5%);
715
+ border-color: color-mix(in srgb, var(--color-ak-glow-primary, #818cf8) 26%, var(--color-ak-border, rgba(255,255,255,0.12)));
716
+ }
717
+ .ak-dark-avatar-tile:is(.dark *),
718
+ .dark .ak-dark-avatar-tile,
719
+ [data-theme="dark"] .ak-dark-avatar-tile,
720
+ [data-ak-dark-panel] .ak-dark-avatar-tile,
721
+ [data-ak-dark-rail] .ak-dark-avatar-tile,
722
+ [data-ak-dark-mobile] .ak-dark-avatar-tile {
723
+ background:
724
+ radial-gradient(circle at 35% 22%, color-mix(in srgb, var(--color-ak-glow-primary, #818cf8) 30%, transparent), transparent 62%),
725
+ color-mix(in srgb, var(--color-ak-primary-container, #31245a) 72%, var(--color-ak-bg, #111) 28%);
726
+ color: var(--color-ak-on-primary-container, var(--color-ak-on-surface, #fff));
727
+ }
728
+ .ak-dark-control:is(button, a, [role="button"], [tabindex]):not([aria-disabled="true"]):not(:disabled):is(:hover, [data-highlighted]),
729
+ .ak-dark-control:is(button, a, [role="button"], [tabindex]):not([aria-disabled="true"]):not(:disabled)[data-highlighted],
730
+ .dark .ak-dark-control:is(button, a, [role="button"], [tabindex]):not([aria-disabled="true"]):not(:disabled):hover,
731
+ [data-theme="dark"] .ak-dark-control:is(button, a, [role="button"], [tabindex]):not([aria-disabled="true"]):not(:disabled):hover,
732
+ [data-ak-dark-panel] .ak-dark-control:is(button, a, [role="button"], [tabindex]):not([aria-disabled="true"]):not(:disabled):hover,
733
+ [data-ak-dark-rail] .ak-dark-control:is(button, a, [role="button"], [tabindex]):not([aria-disabled="true"]):not(:disabled):hover,
734
+ [data-ak-dark-mobile] .ak-dark-control:is(button, a, [role="button"], [tabindex]):not([aria-disabled="true"]):not(:disabled):hover {
735
+ background:
736
+ linear-gradient(135deg, color-mix(in srgb, var(--color-ak-glow-primary, #818cf8) 10%, transparent), transparent 64%),
737
+ color-mix(in srgb, var(--color-ak-on-surface, #fff) 6%, transparent);
738
+ color: var(--color-ak-on-surface, var(--color-ak-text));
739
+ }
740
+ .ak-dark-control-selected:is(.dark *),
741
+ .dark .ak-dark-control-selected,
742
+ [data-theme="dark"] .ak-dark-control-selected,
743
+ [data-ak-dark-panel] .ak-dark-control-selected,
744
+ [data-ak-dark-rail] .ak-dark-control-selected,
745
+ [data-ak-dark-mobile] .ak-dark-control-selected {
746
+ background:
747
+ radial-gradient(circle at 18% 14%, color-mix(in srgb, var(--color-ak-on-primary, #fff) 22%, transparent), transparent 36%),
748
+ linear-gradient(135deg, var(--color-ak-gradient-start, var(--color-ak-primary-hover)) 0%, var(--color-ak-gradient-mid, var(--color-ak-primary)) 52%, var(--color-ak-gradient-end, var(--color-ak-primary-active)) 100%);
749
+ border-color: color-mix(in srgb, var(--color-ak-glow-primary, #818cf8) 64%, var(--color-ak-border, rgba(255,255,255,0.12)));
750
+ box-shadow:
751
+ inset 0 0 0 1px color-mix(in srgb, var(--color-ak-glow-primary, #818cf8) 46%, transparent),
752
+ inset 0 1px 0 color-mix(in srgb, var(--color-ak-neutral-0, #fff) 16%, transparent),
753
+ 0 0 0 1px color-mix(in srgb, var(--color-ak-glow-primary, #818cf8) 14%, transparent),
754
+ 0 12px 28px -22px color-mix(in srgb, var(--color-ak-glow-primary, #818cf8) 78%, transparent);
755
+ color: var(--color-ak-on-primary-selection, var(--color-ak-on-primary, #fff));
756
+ }
757
+ .dark .ak-dark-control-selected.ak-selection-ink,
758
+ [data-theme="dark"] .ak-dark-control-selected.ak-selection-ink,
759
+ [data-ak-dark-panel] .ak-dark-control-selected.ak-selection-ink,
760
+ [data-ak-dark-rail] .ak-dark-control-selected.ak-selection-ink,
761
+ [data-ak-dark-mobile] .ak-dark-control-selected.ak-selection-ink,
762
+ .dark .ak-dark-control-selected :where(.ak-selection-ink, i, svg, .ph),
763
+ [data-theme="dark"] .ak-dark-control-selected :where(.ak-selection-ink, i, svg, .ph),
764
+ [data-ak-dark-panel] .ak-dark-control-selected :where(.ak-selection-ink, i, svg, .ph),
765
+ [data-ak-dark-rail] .ak-dark-control-selected :where(.ak-selection-ink, i, svg, .ph),
766
+ [data-ak-dark-mobile] .ak-dark-control-selected :where(.ak-selection-ink, i, svg, .ph) {
767
+ color: var(--color-ak-on-primary-selection, var(--color-ak-on-primary, #fff));
768
+ }
769
+ .ak-dark-accent-tile:is(.dark *),
770
+ .dark .ak-dark-accent-tile,
771
+ [data-theme="dark"] .ak-dark-accent-tile,
772
+ [data-ak-dark-panel] .ak-dark-accent-tile,
773
+ [data-ak-dark-rail] .ak-dark-accent-tile,
774
+ [data-ak-dark-mobile] .ak-dark-accent-tile {
775
+ background:
776
+ radial-gradient(circle at 22% 16%, color-mix(in srgb, var(--color-ak-on-primary, #fff) 20%, transparent), transparent 38%),
777
+ linear-gradient(135deg, var(--color-ak-gradient-start, var(--color-ak-primary-hover)) 0%, var(--color-ak-gradient-mid, var(--color-ak-primary)) 50%, var(--color-ak-gradient-end, var(--color-ak-primary-active)) 100%);
778
+ border-color: color-mix(in srgb, var(--color-ak-glow-primary, #818cf8) 62%, transparent);
779
+ box-shadow:
780
+ inset 0 0 0 1px color-mix(in srgb, var(--color-ak-on-primary, #fff) 16%, transparent),
781
+ 0 10px 28px -18px color-mix(in srgb, var(--color-ak-glow-primary, #818cf8) 90%, transparent);
782
+ color: var(--color-ak-on-primary, #fff);
783
+ }
784
+ .ak-dark-accent-tile:is(.dark *) :where(i, svg, .ph),
785
+ .dark .ak-dark-accent-tile :where(i, svg, .ph),
786
+ [data-theme="dark"] .ak-dark-accent-tile :where(i, svg, .ph),
787
+ [data-ak-dark-panel] .ak-dark-accent-tile :where(i, svg, .ph),
788
+ [data-ak-dark-rail] .ak-dark-accent-tile :where(i, svg, .ph),
789
+ [data-ak-dark-mobile] .ak-dark-accent-tile :where(i, svg, .ph) {
790
+ color: var(--color-ak-on-primary, #fff);
791
+ }
792
+ .ak-dark-vivid-ink:is(.dark *),
793
+ .dark .ak-dark-vivid-ink,
794
+ [data-theme="dark"] .ak-dark-vivid-ink,
795
+ [data-ak-dark-panel] .ak-dark-vivid-ink,
796
+ [data-ak-dark-rail] .ak-dark-vivid-ink,
797
+ [data-ak-dark-mobile] .ak-dark-vivid-ink {
798
+ color: var(--color-ak-glow-primary, var(--color-ak-primary));
799
+ }
800
+
527
801
  /* ── Focus Ring Utility ── */
528
- .ak-focus-ring:focus-visible { outline: 2px solid var(--color-ak-primary); outline-offset: 2px; }
802
+ .ak-focus-ring:focus-visible {
803
+ /* Material-drivable: packs may restyle focus (neo-brutal = 3px dashed ink).
804
+ Default reproduces the house ring exactly. */
805
+ outline: var(--ak-focus-outline, 2px solid var(--color-ak-border-focus, var(--color-ak-primary)));
806
+ outline-offset: var(--ak-focus-offset, 2px);
807
+ }
529
808
 
530
809
  /* ── Price Fade Transition ── */
531
810
  .ak-price-fade { transition: opacity 0.15s ease-in-out; }
@@ -541,3 +820,309 @@ body.ak-ew-resizing, body.ak-ew-resizing * {
541
820
  @keyframes ak-slideIn { from { opacity: 0; } to { opacity: 1; } }
542
821
  @keyframes ak-fadeOut { from { opacity: 1; } to { opacity: 0; } }
543
822
  }
823
+
824
+ /* ── Typography roles (2026-08-02) ─────────────────────────────────
825
+ Five roles, each an authored family+weight pair driven by variables so a
826
+ font pairing re-tunes the whole hierarchy coherently (family swaps without
827
+ weight compensation always look wrong — different families have different
828
+ weight curves). Components consume ROLE classes, never literal
829
+ font-family/font-weight for themed text. Weight vars live outside the
830
+ --font-weight-* namespace on purpose (v4 would generate colliding font-*
831
+ utilities). Mono role = the existing font-ak-mono utility. */
832
+ .ak-font-heading {
833
+ font-family: var(--font-ak-heading, var(--font-ak-sans, Inter, sans-serif));
834
+ font-weight: var(--ak-weight-heading, 600);
835
+ }
836
+ .ak-font-body {
837
+ font-family: var(--font-ak-sans, Inter, sans-serif);
838
+ font-weight: var(--ak-weight-body, 400);
839
+ }
840
+ .ak-font-ui {
841
+ font-family: var(--font-ak-ui, var(--font-ak-sans, Inter, sans-serif));
842
+ font-weight: var(--ak-weight-ui, 500);
843
+ }
844
+ /* Emphasized UI — buttons, pressed/active controls, row titles. The library
845
+ calibration is semibold on every button and active state (conventions:
846
+ "titles are font-semibold"; forms references all carry font-semibold CTAs)
847
+ while quiet UI (menu rows, inputs, nav) stays at the ui weight. Pairings
848
+ override via --ak-weight-ui-strong. */
849
+ .ak-font-ui-strong {
850
+ font-family: var(--font-ak-ui, var(--font-ak-sans, Inter, sans-serif));
851
+ font-weight: var(--ak-weight-ui-strong, 600);
852
+ }
853
+ .ak-font-label {
854
+ font-family: var(--font-ak-label, var(--font-ak-sans, Inter, sans-serif));
855
+ font-weight: var(--ak-weight-label, 600);
856
+ letter-spacing: var(--ak-tracking-label, 0.02em);
857
+ }
858
+
859
+ /* ── Material recipes (2026-08-02) ──────────────────────────────
860
+ The consumption layer for material appearances (astralkit/presets
861
+ MATERIAL_APPEARANCES). Components carry these classes; appearance packs
862
+ fill the --ak-* slots. Every default reproduces the pre-material look,
863
+ so unthemed apps are pixel-identical. */
864
+ .ak-material-control {
865
+ box-shadow: var(--ak-control-shadow, none);
866
+ transition: box-shadow 0.15s ease, transform 0.15s ease;
867
+ }
868
+ .ak-material-control:hover {
869
+ transform: var(--ak-control-hover-transform, none);
870
+ box-shadow: var(--ak-control-shadow-hover, var(--ak-control-shadow, none));
871
+ }
872
+ .ak-material-control:active {
873
+ box-shadow: var(--ak-control-shadow-active, var(--ak-control-shadow, none));
874
+ }
875
+ .ak-material-surface {
876
+ position: relative;
877
+ /* Two-layer background: an optional SHEEN (falls off from the single
878
+ top-left light source) over the fill. Defaults collapse to plain fill. */
879
+ background: var(--ak-surface-sheen, none), var(--ak-surface-fill, var(--color-ak-surface));
880
+ border: var(--ak-surface-border-w, 1px) solid var(--ak-surface-border-color, var(--color-ak-border));
881
+ box-shadow: var(--ak-surface-shadow, none);
882
+ -webkit-backdrop-filter: var(--ak-surface-blur, none);
883
+ backdrop-filter: var(--ak-surface-blur, none);
884
+ }
885
+ /* Directional rim — a gradient border ring (hot toward the light source,
886
+ dying opposite) via padding + mask-composite. Invisible until a pack sets
887
+ --ak-surface-rim. */
888
+ .ak-material-surface::before {
889
+ content: "";
890
+ position: absolute;
891
+ inset: 0;
892
+ border-radius: inherit;
893
+ padding: var(--ak-surface-rim-w, 1.5px);
894
+ background: var(--ak-surface-rim, none);
895
+ -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
896
+ -webkit-mask-composite: xor;
897
+ mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
898
+ mask-composite: exclude;
899
+ pointer-events: none;
900
+ }
901
+ /* Depth ladder: nested material surfaces step their tint up one level so
902
+ stacked glass reads as stacked panes. Packs opt in via the -2 slot. */
903
+ .ak-material-surface .ak-material-surface {
904
+ background: var(--ak-surface-sheen-2, var(--ak-surface-sheen, none)),
905
+ var(--ak-surface-fill-2, var(--ak-surface-fill, var(--color-ak-surface-2)));
906
+ box-shadow: var(--ak-surface-shadow-2, var(--ak-surface-shadow, none));
907
+ }
908
+
909
+ /* ── Small-control material parts (batch 2) ─────────────────────────────
910
+ Glass Law 4: glass is for surfaces — active parts stay flat solid. The
911
+ knob (switch), thumb (slider) and track are material-slotted so the
912
+ Appearance dial re-materializes running form controls. Position is done
913
+ with the CSS `translate` property in components; recipes only ever touch
914
+ `transform`, so squish/tilt compose with position instead of clobbering it. */
915
+ .ak-control-knob {
916
+ background: var(--ak-knob-fill, #ffffff);
917
+ box-shadow: var(--ak-knob-shadow, 0 1px 3px rgba(0, 0, 0, 0.25));
918
+ transition:
919
+ translate 200ms var(--ak-control-spring, cubic-bezier(0.4, 0, 0.2, 1)),
920
+ transform 200ms var(--ak-control-spring, cubic-bezier(0.4, 0, 0.2, 1)),
921
+ background-color 150ms ease;
922
+ }
923
+ [data-state="checked"] > .ak-control-knob {
924
+ background: var(--ak-knob-fill-checked, var(--ak-knob-fill, #ffffff));
925
+ }
926
+ /* The squish: origin follows the travel direction so the stretch reads as
927
+ the knob leaning into the push. */
928
+ [data-ak-component="switch"]:active .ak-control-knob {
929
+ transform: var(--ak-knob-press, scaleX(1.12));
930
+ transform-origin: left center;
931
+ }
932
+ [data-ak-component="switch"][data-state="checked"]:active .ak-control-knob {
933
+ transform-origin: right center;
934
+ }
935
+ .ak-control-thumb {
936
+ background: var(--ak-thumb-fill, #ffffff);
937
+ border-radius: var(--ak-thumb-radius, 9999px);
938
+ border: var(--ak-thumb-border-w, 0px) solid var(--ak-thumb-border-color, transparent);
939
+ box-shadow: var(--ak-thumb-shadow, 0 1px 4px rgba(0, 0, 0, 0.3));
940
+ transition: transform 180ms var(--ak-control-spring, cubic-bezier(0.4, 0, 0.2, 1)),
941
+ box-shadow 180ms ease;
942
+ }
943
+ .ak-control-thumb:hover {
944
+ transform: var(--ak-thumb-hover-transform, scale(1.1));
945
+ }
946
+ .ak-control-thumb:active {
947
+ transform: var(--ak-thumb-active-transform, scale(0.96));
948
+ }
949
+ /* Track border renders as an INSET RING, not a real border — layout never
950
+ shifts, so knob travel constants hold across every material. */
951
+ .ak-control-track {
952
+ box-shadow:
953
+ inset 0 0 0 var(--ak-track-border-w, 0px) var(--ak-track-border-color, transparent),
954
+ var(--ak-track-shadow, 0 0 0 0 transparent);
955
+ }
956
+ @media (prefers-reduced-motion: reduce) {
957
+ .ak-control-knob,
958
+ .ak-control-thumb {
959
+ transition: none;
960
+ }
961
+ }
962
+
963
+ /* ── Themed scrollbar (2026-08-03) ──────────────────────────────────────
964
+ A thin, ALWAYS-VISIBLE scrollbar on the token layer — thumb follows the
965
+ theme (border-strong, text-muted on hover), track transparent. Visible
966
+ at rest on purpose: hidden-until-hover scrollbars fail discoverability.
967
+ Firefox via scrollbar-*, WebKit via the pseudo-elements. */
968
+ .ak-scrollbar {
969
+ scrollbar-width: thin;
970
+ scrollbar-color: var(--color-ak-border-strong) transparent;
971
+ }
972
+ .ak-scrollbar::-webkit-scrollbar {
973
+ width: 8px;
974
+ height: 8px;
975
+ }
976
+ .ak-scrollbar::-webkit-scrollbar-thumb {
977
+ background: var(--color-ak-border-strong);
978
+ border-radius: 9999px;
979
+ }
980
+ .ak-scrollbar::-webkit-scrollbar-thumb:hover {
981
+ background: var(--color-ak-text-muted);
982
+ }
983
+ .ak-scrollbar::-webkit-scrollbar-track {
984
+ background: transparent;
985
+ }
986
+
987
+ /* ── Quiet-hover bar (2026-08-03) ───────────────────────────────────────
988
+ The hover treatment for quiet controls and text links: a bar under the
989
+ WHOLE control (::after), not text-decoration — underlines don't propagate
990
+ into flex children, so any link with an icon shows a cut-off underline.
991
+ Materials fill the slots (brutal: 3px ink bar offset 4px = the backface's
992
+ bottom edge). Defaults are 0/invisible; a component can opt into a
993
+ default bar via the -default fallback vars (the link button does), which
994
+ materials still override via the primary vars. */
995
+ .ak-quiet-hover {
996
+ position: relative;
997
+ }
998
+ .ak-quiet-hover::after {
999
+ content: "";
1000
+ position: absolute;
1001
+ inset-inline: 0;
1002
+ bottom: calc(-1 * var(--ak-quiet-hover-offset, var(--ak-quiet-hover-offset-default, 0px)));
1003
+ height: var(--ak-quiet-hover-thickness, var(--ak-quiet-hover-thickness-default, 0px));
1004
+ background: var(--ak-quiet-hover-color, currentColor);
1005
+ opacity: 0;
1006
+ transition: opacity 120ms ease;
1007
+ pointer-events: none;
1008
+ }
1009
+ .ak-quiet-hover:hover::after,
1010
+ .ak-quiet-hover:focus-visible::after {
1011
+ opacity: 1;
1012
+ }
1013
+ @media (prefers-reduced-motion: reduce) {
1014
+ .ak-quiet-hover::after {
1015
+ transition: none;
1016
+ }
1017
+ }
1018
+
1019
+ /* ── Overlay motion (core batch 4) ──────────────────────────────────────
1020
+ In/out animations for portalled overlays, driven by Radix data-state.
1021
+ Plain CSS so both Tailwind pipelines get them. Dialogs center with
1022
+ inset-0 + m-auto (NOT translate) so the pop transform never fights
1023
+ positioning; sheets own their transform entirely. */
1024
+ @keyframes ak-overlay-in { from { opacity: 0; } to { opacity: 1; } }
1025
+ @keyframes ak-overlay-out { from { opacity: 1; } to { opacity: 0; } }
1026
+ @keyframes ak-pop-in { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: scale(1); } }
1027
+ @keyframes ak-pop-out { from { opacity: 1; transform: scale(1); } to { opacity: 0; transform: scale(0.96); } }
1028
+ @keyframes ak-slide-in-right { from { transform: translateX(100%); } to { transform: translateX(0); } }
1029
+ @keyframes ak-slide-out-right { from { transform: translateX(0); } to { transform: translateX(100%); } }
1030
+ @keyframes ak-slide-in-left { from { transform: translateX(-100%); } to { transform: translateX(0); } }
1031
+ @keyframes ak-slide-out-left { from { transform: translateX(0); } to { transform: translateX(-100%); } }
1032
+ @keyframes ak-slide-in-bottom { from { transform: translateY(100%); } to { transform: translateY(0); } }
1033
+ @keyframes ak-slide-out-bottom { from { transform: translateY(0); } to { transform: translateY(100%); } }
1034
+ @keyframes ak-slide-in-top { from { transform: translateY(-100%); } to { transform: translateY(0); } }
1035
+ @keyframes ak-slide-out-top { from { transform: translateY(0); } to { transform: translateY(-100%); } }
1036
+ .ak-overlay-anim[data-state="open"] { animation: ak-overlay-in 150ms ease; }
1037
+ .ak-overlay-anim[data-state="closed"] { animation: ak-overlay-out 130ms ease forwards; }
1038
+ .ak-pop-anim[data-state="open"],
1039
+ .ak-pop-anim[data-state="delayed-open"] { animation: ak-pop-in 160ms cubic-bezier(0.16, 1, 0.3, 1); }
1040
+ .ak-pop-anim[data-state="closed"] { animation: ak-pop-out 120ms ease forwards; }
1041
+ .ak-sheet-anim[data-state="open"][data-side="right"] { animation: ak-slide-in-right 260ms cubic-bezier(0.16, 1, 0.3, 1); }
1042
+ .ak-sheet-anim[data-state="closed"][data-side="right"] { animation: ak-slide-out-right 200ms ease forwards; }
1043
+ .ak-sheet-anim[data-state="open"][data-side="left"] { animation: ak-slide-in-left 260ms cubic-bezier(0.16, 1, 0.3, 1); }
1044
+ .ak-sheet-anim[data-state="closed"][data-side="left"] { animation: ak-slide-out-left 200ms ease forwards; }
1045
+ .ak-sheet-anim[data-state="open"][data-side="bottom"] { animation: ak-slide-in-bottom 260ms cubic-bezier(0.16, 1, 0.3, 1); }
1046
+ .ak-sheet-anim[data-state="closed"][data-side="bottom"] { animation: ak-slide-out-bottom 200ms ease forwards; }
1047
+ .ak-sheet-anim[data-state="open"][data-side="top"] { animation: ak-slide-in-top 260ms cubic-bezier(0.16, 1, 0.3, 1); }
1048
+ .ak-sheet-anim[data-state="closed"][data-side="top"] { animation: ak-slide-out-top 200ms ease forwards; }
1049
+ /* Height-reveal for Radix Accordion/Collapsible content. */
1050
+ @keyframes ak-accordion-down { from { height: 0; } to { height: var(--radix-accordion-content-height); } }
1051
+ @keyframes ak-accordion-up { from { height: var(--radix-accordion-content-height); } to { height: 0; } }
1052
+ @keyframes ak-collapsible-down { from { height: 0; } to { height: var(--radix-collapsible-content-height); } }
1053
+ @keyframes ak-collapsible-up { from { height: var(--radix-collapsible-content-height); } to { height: 0; } }
1054
+ .ak-accordion-anim[data-state="open"] { animation: ak-accordion-down 200ms cubic-bezier(0.16, 1, 0.3, 1); }
1055
+ .ak-accordion-anim[data-state="closed"] { animation: ak-accordion-up 180ms ease forwards; }
1056
+ .ak-collapsible-anim[data-state="open"] { animation: ak-collapsible-down 200ms cubic-bezier(0.16, 1, 0.3, 1); }
1057
+ .ak-collapsible-anim[data-state="closed"] { animation: ak-collapsible-up 180ms ease forwards; }
1058
+ @media (prefers-reduced-motion: reduce) {
1059
+ .ak-overlay-anim,
1060
+ .ak-pop-anim,
1061
+ .ak-sheet-anim,
1062
+ .ak-accordion-anim,
1063
+ .ak-collapsible-anim {
1064
+ animation-duration: 0.01ms !important;
1065
+ }
1066
+ }
1067
+
1068
+ /* ── Button group (core) ────────────────────────────────────────────────
1069
+ Container-aware styling for attached button groups (the CORE-STANDARD
1070
+ pattern: plain CSS attribute selectors, works in both Tailwind pipelines).
1071
+ Inner corners collapse, outer corners keep the radius tokens, adjacent
1072
+ borders overlap by 1px, and the focused member wins the z-order. */
1073
+ /* Any direct child participates in the corner system — buttons today,
1074
+ inputs/selects/ButtonGroupText when those cores land. Separators opt out. */
1075
+ .ak-button-group {
1076
+ display: inline-flex;
1077
+ align-items: stretch;
1078
+ }
1079
+ .ak-button-group > :focus-visible {
1080
+ z-index: 1;
1081
+ }
1082
+ .ak-button-group[data-ak-orientation="vertical"] {
1083
+ flex-direction: column;
1084
+ }
1085
+ /* Fusing applies only to a leaf group — a group of groups is a toolbar
1086
+ (below) and its sub-groups keep their own corners. */
1087
+ .ak-button-group:not(:has(> .ak-button-group)) > * {
1088
+ position: relative;
1089
+ border-radius: 0;
1090
+ }
1091
+ .ak-button-group[data-ak-orientation="horizontal"]:not(:has(> .ak-button-group)) > * + :not(.ak-button-group-separator) {
1092
+ margin-left: -1px;
1093
+ }
1094
+ .ak-button-group[data-ak-orientation="horizontal"]:not(:has(> .ak-button-group)) > .ak-button-group-separator + * {
1095
+ margin-left: 0;
1096
+ }
1097
+ .ak-button-group[data-ak-orientation="horizontal"]:not(:has(> .ak-button-group)) > :first-child {
1098
+ border-top-left-radius: var(--radius-ak-md);
1099
+ border-bottom-left-radius: var(--radius-ak-md);
1100
+ }
1101
+ .ak-button-group[data-ak-orientation="horizontal"]:not(:has(> .ak-button-group)) > :last-child {
1102
+ border-top-right-radius: var(--radius-ak-md);
1103
+ border-bottom-right-radius: var(--radius-ak-md);
1104
+ }
1105
+ .ak-button-group[data-ak-orientation="vertical"]:not(:has(> .ak-button-group)) > * + :not(.ak-button-group-separator) {
1106
+ margin-top: -1px;
1107
+ }
1108
+ .ak-button-group[data-ak-orientation="vertical"]:not(:has(> .ak-button-group)) > .ak-button-group-separator + * {
1109
+ margin-top: 0;
1110
+ }
1111
+ .ak-button-group[data-ak-orientation="vertical"]:not(:has(> .ak-button-group)) > :first-child {
1112
+ border-top-left-radius: var(--radius-ak-md);
1113
+ border-top-right-radius: var(--radius-ak-md);
1114
+ }
1115
+ .ak-button-group[data-ak-orientation="vertical"]:not(:has(> .ak-button-group)) > :last-child {
1116
+ border-bottom-left-radius: var(--radius-ak-md);
1117
+ border-bottom-right-radius: var(--radius-ak-md);
1118
+ }
1119
+ /* The seam flips with the group's axis. */
1120
+ .ak-button-group[data-ak-orientation="vertical"] > .ak-button-group-separator {
1121
+ width: auto;
1122
+ height: 1px;
1123
+ align-self: stretch;
1124
+ }
1125
+ /* A group of groups is a toolbar: sub-groups space out instead of fusing. */
1126
+ .ak-button-group:has(> .ak-button-group) {
1127
+ gap: var(--spacing-ak-1, 0.5rem);
1128
+ }
@@ -0,0 +1,12 @@
1
+ export type AppearancePresetId = 'clean' | 'soft-mesh' | 'neon-edge' | 'cinematic-backlight' | 'circuit-spark' | 'portfolio-grain';
2
+ type AppearanceVariableName = `--ak-effect-${string}` | `--gradient-ak-${string}`;
3
+ export interface AppearancePreset {
4
+ id: AppearancePresetId;
5
+ name: string;
6
+ description: string;
7
+ variables: Partial<Record<AppearanceVariableName, string>>;
8
+ }
9
+ export declare const DEFAULT_APPEARANCE_PRESET_ID: AppearancePresetId;
10
+ export declare const APPEARANCE_PRESETS: AppearancePreset[];
11
+ export declare function appearancePresetToVariables(preset: AppearancePreset): Partial<Record<AppearanceVariableName, string>>;
12
+ export {};