@luxfi/ui 7.4.6 → 7.4.7
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/dist/alert.cjs +3 -7
- package/dist/alert.js +3 -7
- package/dist/auth.cjs +481 -0
- package/dist/auth.d.cts +80 -0
- package/dist/auth.d.ts +80 -0
- package/dist/auth.js +456 -0
- package/dist/badge.cjs +4 -8
- package/dist/badge.js +4 -8
- package/dist/button.cjs +3 -7
- package/dist/button.js +3 -7
- package/dist/collapsible.cjs +3 -7
- package/dist/collapsible.js +3 -7
- package/dist/icon-button.cjs +3 -7
- package/dist/icon-button.js +3 -7
- package/dist/icons.cjs +0 -1
- package/dist/icons.js +0 -1
- package/dist/image.cjs +3 -7
- package/dist/image.js +3 -7
- package/dist/index.cjs +9 -8
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +9 -9
- package/dist/link.cjs +3 -7
- package/dist/link.js +3 -7
- package/dist/next.cjs +11 -1
- package/dist/next.js +11 -1
- package/dist/select.cjs +3 -7
- package/dist/select.js +3 -7
- package/dist/skeleton.cjs +8 -7
- package/dist/skeleton.d.cts +17 -1
- package/dist/skeleton.d.ts +17 -1
- package/dist/skeleton.js +8 -8
- package/dist/table.cjs +3 -7
- package/dist/table.js +3 -7
- package/dist/tag.cjs +4 -8
- package/dist/tag.js +4 -8
- package/dist/tooltip.cjs +1 -1
- package/dist/tooltip.js +1 -1
- package/dist/vite.cjs +4 -3
- package/dist/vite.js +4 -3
- package/package.json +12 -2
- package/src/auth.tsx +198 -0
- package/src/engine.ts +24 -3
- package/src/index.ts +1 -1
- package/src/next.ts +10 -0
- package/src/skeleton.tsx +36 -7
- package/src/tooltip.tsx +13 -5
- package/src/vite.ts +4 -9
- package/tokens.css +217 -0
package/src/vite.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// Node-only (it is a build-time module) and dependency-free: it returns a plain
|
|
15
15
|
// Vite plugin object, so it does not force `vite` into anyone's runtime graph.
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { ENGINE_DEDUPE, GUI_DEFINES } from './engine';
|
|
18
18
|
|
|
19
19
|
interface LuxUiViteOptions {
|
|
20
20
|
/**
|
|
@@ -42,14 +42,9 @@ interface LuxUiVitePlugin {
|
|
|
42
42
|
// The WHOLE engine, not a hand-picked three. `@hanzogui/popper` and
|
|
43
43
|
// `@hanzogui/popover` publish the contexts a Tooltip trigger reads, and they
|
|
44
44
|
// were the two missing from the old list — so a consumer using this plugin
|
|
45
|
-
// could still get two poppers and the `setReference` crash it causes.
|
|
46
|
-
// shared with the Next wrapper.
|
|
47
|
-
const DEDUPE =
|
|
48
|
-
'react',
|
|
49
|
-
'react-dom',
|
|
50
|
-
'@tanstack/react-query',
|
|
51
|
-
...GUI_PACKAGES,
|
|
52
|
-
];
|
|
45
|
+
// could still get two poppers and the `setReference` crash it causes. The list
|
|
46
|
+
// itself lives in ./engine, shared with the Next wrapper.
|
|
47
|
+
const DEDUPE = ENGINE_DEDUPE;
|
|
53
48
|
|
|
54
49
|
const INCLUDE = [
|
|
55
50
|
'react-native-web',
|
package/tokens.css
CHANGED
|
@@ -730,3 +730,220 @@
|
|
|
730
730
|
--sidebar-border: var(--lux-paper-10);
|
|
731
731
|
--sidebar-ring: var(--lux-n-500);
|
|
732
732
|
}
|
|
733
|
+
|
|
734
|
+
/* ================================================================
|
|
735
|
+
6 — MOTION. One system: five durations, four curves, six moves.
|
|
736
|
+
|
|
737
|
+
Two rules make "smooth" measurable. Both were established by
|
|
738
|
+
benchmark, not by taste, and the second one is the one that bites.
|
|
739
|
+
|
|
740
|
+
1. WHAT you animate. A decorative animation may touch `transform`
|
|
741
|
+
and `opacity` and nothing else. Those two are the only properties
|
|
742
|
+
the compositor can run off the main thread, so they survive a
|
|
743
|
+
busy CPU. Animating width / height / top / left / margin /
|
|
744
|
+
background-position forces layout or paint every frame. Worse,
|
|
745
|
+
the layout ones also register as layout shift: the explorer's
|
|
746
|
+
mobile drawer animated `left` and scored CLS 0.726 every time it
|
|
747
|
+
opened — Google calls anything over 0.25 "poor". The same drawer
|
|
748
|
+
on `transform` scores 0.
|
|
749
|
+
|
|
750
|
+
2. HOW MANY things animate. Keep it O(1) in the size of the page.
|
|
751
|
+
Every independently animated element is a compositor layer to
|
|
752
|
+
allocate, rasterise and composite. Past a few hundred, that count
|
|
753
|
+
— not the property — is the bottleneck, and rule 1 alone will
|
|
754
|
+
happily walk you off a cliff. The measurements are in 6c.
|
|
755
|
+
|
|
756
|
+
The one deliberate exception is a disclosure widget (accordion,
|
|
757
|
+
collapsible). There the box change IS the content the user asked
|
|
758
|
+
for, not decoration, so animating its height is correct. Decoration
|
|
759
|
+
animates transform; disclosure animates the box it discloses.
|
|
760
|
+
|
|
761
|
+
Durations are named for their ROLE so a caller cannot pick "300ms
|
|
762
|
+
because it felt right". Exits are shorter than entrances — by the
|
|
763
|
+
time a thing leaves the user has already decided, and waiting on it
|
|
764
|
+
reads as lag.
|
|
765
|
+
================================================================ */
|
|
766
|
+
:root {
|
|
767
|
+
/* --- Durations -------------------------------------------------- */
|
|
768
|
+
--lux-dur-press: 80ms; /* tap acknowledgement */
|
|
769
|
+
--lux-dur-hover: 120ms; /* pointer feedback */
|
|
770
|
+
--lux-dur-exit: 140ms; /* something leaves */
|
|
771
|
+
--lux-dur-enter: 200ms; /* something appears */
|
|
772
|
+
--lux-dur-move: 280ms; /* a panel travels across the viewport */
|
|
773
|
+
|
|
774
|
+
/* --- Curves ----------------------------------------------------- */
|
|
775
|
+
--lux-ease: cubic-bezier(0.4, 0, 0.2, 1); /* A -> B */
|
|
776
|
+
--lux-ease-out: cubic-bezier(0.16, 1, 0.3, 1); /* entering */
|
|
777
|
+
--lux-ease-in: cubic-bezier(0.7, 0, 0.84, 0); /* leaving */
|
|
778
|
+
--lux-ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1); /* press */
|
|
779
|
+
|
|
780
|
+
/* --- Distances. Enter offsets are one value, not a per-component
|
|
781
|
+
guess. Both collapse to 0 under reduced motion. -------------- */
|
|
782
|
+
--lux-lift: 2px; /* hover raise */
|
|
783
|
+
--lux-rise: 8px; /* enter offset */
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/* --- The six moves. transform + opacity only. --------------------- */
|
|
787
|
+
@keyframes lux-fade-in { from { opacity: 0 } to { opacity: 1 } }
|
|
788
|
+
@keyframes lux-fade-out { from { opacity: 1 } to { opacity: 0 } }
|
|
789
|
+
|
|
790
|
+
@keyframes lux-rise-in {
|
|
791
|
+
from { opacity: 0; transform: translate3d(0, var(--lux-rise), 0) }
|
|
792
|
+
to { opacity: 1; transform: translate3d(0, 0, 0) }
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
@keyframes lux-scale-in {
|
|
796
|
+
from { opacity: 0; transform: scale(0.96) }
|
|
797
|
+
to { opacity: 1; transform: scale(1) }
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
@keyframes lux-scale-out {
|
|
801
|
+
from { opacity: 1; transform: scale(1) }
|
|
802
|
+
to { opacity: 0; transform: scale(0.96) }
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/* The loading sweep — ONE per page, never one per skeleton. See 6c. */
|
|
806
|
+
@keyframes lux-sweep {
|
|
807
|
+
from { transform: translate3d(-100%, 0, 0) }
|
|
808
|
+
to { transform: translate3d(350%, 0, 0) }
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
@keyframes lux-spin { to { transform: rotate(360deg) } }
|
|
812
|
+
|
|
813
|
+
/* ================================================================
|
|
814
|
+
6b — The primitives. Every surface reaches for these, never for a
|
|
815
|
+
hand-rolled keyframe.
|
|
816
|
+
================================================================ */
|
|
817
|
+
|
|
818
|
+
.lux-enter { animation: lux-rise-in var(--lux-dur-enter) var(--lux-ease-out) both }
|
|
819
|
+
.lux-exit { animation: lux-fade-out var(--lux-dur-exit) var(--lux-ease-in) both }
|
|
820
|
+
|
|
821
|
+
/* Floating surfaces — menu, popover, tooltip, dialog. They grow from
|
|
822
|
+
where they were summoned rather than sliding in from nowhere. */
|
|
823
|
+
.lux-pop { animation: lux-scale-in var(--lux-dur-enter) var(--lux-ease-out) both }
|
|
824
|
+
.lux-pop-exit { animation: lux-scale-out var(--lux-dur-exit) var(--lux-ease-in) both }
|
|
825
|
+
|
|
826
|
+
/* A list cascades by setting --lux-i on each row; one property, no JS. */
|
|
827
|
+
.lux-stagger { animation-delay: calc(var(--lux-i, 0) * 40ms) }
|
|
828
|
+
|
|
829
|
+
/* Hover / press feedback. `transform` and `opacity` are the only
|
|
830
|
+
transitioned properties, so this is safe on any element. */
|
|
831
|
+
.lux-interactive {
|
|
832
|
+
transition:
|
|
833
|
+
transform var(--lux-dur-hover) var(--lux-ease-out),
|
|
834
|
+
opacity var(--lux-dur-hover) var(--lux-ease-out);
|
|
835
|
+
}
|
|
836
|
+
.lux-interactive:hover { transform: translate3d(0, calc(-1 * var(--lux-lift)), 0) }
|
|
837
|
+
.lux-interactive:active { transform: scale(0.98); transition-duration: var(--lux-dur-press) }
|
|
838
|
+
.lux-interactive:disabled,
|
|
839
|
+
.lux-interactive[aria-disabled='true'] { transform: none }
|
|
840
|
+
|
|
841
|
+
/* ================================================================
|
|
842
|
+
6c — The loading state. Two primitives, one job each.
|
|
843
|
+
|
|
844
|
+
`.lux-skeleton` RESERVES SPACE. `.lux-loading` SAYS WORK IS IN
|
|
845
|
+
FLIGHT. Keeping those apart is what makes the whole thing cheap,
|
|
846
|
+
because only the second one moves — once for the page, not once per
|
|
847
|
+
skeleton.
|
|
848
|
+
|
|
849
|
+
This is the part that is counter-intuitive, so here are the numbers
|
|
850
|
+
that settled it. Chromium, 390x844 @3x, 4x CPU throttle, 5s samples,
|
|
851
|
+
n = concurrently-mounted skeletons:
|
|
852
|
+
|
|
853
|
+
n=600 bgpos 79.6fps sheen 57.2fps pulse 115.4fps bar 120fps
|
|
854
|
+
n=1460 bgpos 33.6fps sheen 23.0fps pulse 39.0fps bar 120fps
|
|
855
|
+
n=3000 bgpos 13.8fps sheen 5.4fps pulse 18.2fps bar 120fps
|
|
856
|
+
|
|
857
|
+
bgpos = per-element background-position gradient (what we shipped)
|
|
858
|
+
sheen = per-element transform on a ::after (the "obvious" fix)
|
|
859
|
+
pulse = per-element opacity keyframe
|
|
860
|
+
bar = flat skeletons + ONE page-level sweep (what we ship now)
|
|
861
|
+
|
|
862
|
+
Read the middle column twice. Rewriting the shimmer as a per-element
|
|
863
|
+
`transform` — obeying "animate transform and opacity only" to the
|
|
864
|
+
letter — made it 32% SLOWER than the background-position version it
|
|
865
|
+
replaced, and at n=3000 it produced a single 2.5-SECOND frame.
|
|
866
|
+
|
|
867
|
+
The property rule is necessary and not sufficient. Past a few
|
|
868
|
+
hundred elements the dominant cost is the NUMBER of independently
|
|
869
|
+
animated elements: each one is a compositor layer to allocate,
|
|
870
|
+
rasterise and composite, and `overflow:hidden` on each adds a
|
|
871
|
+
render surface on top. Only the O(1) design is flat in n — 120fps
|
|
872
|
+
at 600, at 1460 and at 3000, identical to animating nothing.
|
|
873
|
+
|
|
874
|
+
So: a skeleton is a promise about layout and nothing else. It
|
|
875
|
+
occupies exactly the space the real content will (zero CLS) and it
|
|
876
|
+
holds still.
|
|
877
|
+
================================================================ */
|
|
878
|
+
.lux-skeleton {
|
|
879
|
+
background: var(--color-skeleton-start);
|
|
880
|
+
/* Nothing inside a skeleton is readable yet; say so once, here. */
|
|
881
|
+
color: transparent;
|
|
882
|
+
user-select: none;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
/* The one moving part. Fixed, 2px, above everything, out of flow — so it
|
|
886
|
+
cannot shift layout and cannot tint content. Mount it while any request
|
|
887
|
+
is in flight; unmount it when they land. One per page. */
|
|
888
|
+
.lux-loading {
|
|
889
|
+
position: fixed;
|
|
890
|
+
inset: 0 0 auto;
|
|
891
|
+
height: 2px;
|
|
892
|
+
z-index: 60;
|
|
893
|
+
overflow: hidden;
|
|
894
|
+
background: var(--color-skeleton-start);
|
|
895
|
+
pointer-events: none;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
.lux-loading::after {
|
|
899
|
+
content: '';
|
|
900
|
+
position: absolute;
|
|
901
|
+
inset: 0;
|
|
902
|
+
width: 40%;
|
|
903
|
+
background: linear-gradient(
|
|
904
|
+
90deg,
|
|
905
|
+
transparent 0%,
|
|
906
|
+
var(--color-skeleton-end) 50%,
|
|
907
|
+
transparent 100%
|
|
908
|
+
);
|
|
909
|
+
animation: lux-sweep 1.1s var(--lux-ease) infinite;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/* ================================================================
|
|
913
|
+
6d — Reduced motion.
|
|
914
|
+
|
|
915
|
+
Belt AND braces, because either alone leaks:
|
|
916
|
+
- zeroing the TOKENS catches anything that reads them, including
|
|
917
|
+
inline styles and the Web Animations API, which !important
|
|
918
|
+
cannot reach;
|
|
919
|
+
- the global override catches every hand-rolled transition that
|
|
920
|
+
never adopted a token.
|
|
921
|
+
Unscoped on purpose. A reduced-motion rule that only applies inside
|
|
922
|
+
one component is a rule the rest of the app ignores.
|
|
923
|
+
|
|
924
|
+
Motion goes; FEEDBACK stays. State changes still land, instantly.
|
|
925
|
+
================================================================ */
|
|
926
|
+
@media (prefers-reduced-motion: reduce) {
|
|
927
|
+
:root {
|
|
928
|
+
--lux-dur-press: 1ms;
|
|
929
|
+
--lux-dur-hover: 1ms;
|
|
930
|
+
--lux-dur-exit: 1ms;
|
|
931
|
+
--lux-dur-enter: 1ms;
|
|
932
|
+
--lux-dur-move: 1ms;
|
|
933
|
+
--lux-lift: 0px;
|
|
934
|
+
--lux-rise: 0px;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
*,
|
|
938
|
+
*::before,
|
|
939
|
+
*::after {
|
|
940
|
+
animation-duration: 1ms !important;
|
|
941
|
+
animation-iteration-count: 1 !important;
|
|
942
|
+
transition-duration: 1ms !important;
|
|
943
|
+
scroll-behavior: auto !important;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
/* A sweep that runs once and freezes mid-track is a smear. Drop the
|
|
947
|
+
motion; the bar itself stays, so "work in flight" is still visible. */
|
|
948
|
+
.lux-loading::after { animation: none; width: 100%; opacity: 0.5 }
|
|
949
|
+
}
|