From 2c56cb021ff420c5b57c3e6b0464bc701861bfc8 Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Fri, 3 Apr 2026 19:38:37 +0300 Subject: [PATCH] refactor: switch shine text to CSS shimmer --- packages/ui/src/components/ui/text.tsx | 28 ++++---- packages/ui/src/index.css | 95 ++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 16 deletions(-) diff --git a/packages/ui/src/components/ui/text.tsx b/packages/ui/src/components/ui/text.tsx index 74bb5b23..537a25ad 100644 --- a/packages/ui/src/components/ui/text.tsx +++ b/packages/ui/src/components/ui/text.tsx @@ -21,23 +21,19 @@ const variants = [ { variant: "shine", component: ({ children, className, ...props }) => ( - - {children} - + + {children} + + + ), }, { diff --git a/packages/ui/src/index.css b/packages/ui/src/index.css index 9f5afaaa..60530bca 100644 --- a/packages/ui/src/index.css +++ b/packages/ui/src/index.css @@ -287,6 +287,101 @@ textarea[data-chat-input="true"]:focus-visible { } } +[data-component="oc-text-shimmer"] { + --oc-text-shimmer-step: 45ms; + --oc-text-shimmer-duration: 1200ms; + --oc-text-shimmer-angle: 90deg; + --oc-text-shimmer-spread: 5.2ch; + --oc-text-shimmer-size: 360%; + --oc-text-shimmer-base-color: color-mix(in srgb, var(--surface-muted-foreground) 75%, transparent); + --oc-text-shimmer-peak-color: var(--surface-foreground); + --oc-text-shimmer-sweep: linear-gradient( + var(--oc-text-shimmer-angle), + transparent calc(50% - var(--oc-text-shimmer-spread)), + var(--oc-text-shimmer-peak-color) 50%, + transparent calc(50% + var(--oc-text-shimmer-spread)) + ); + --oc-text-shimmer-base: linear-gradient(var(--oc-text-shimmer-base-color), var(--oc-text-shimmer-base-color)); + +} + +[data-component="oc-text-shimmer"] [data-slot="text-shimmer-char"] { + display: inline-grid; + white-space: inherit; + font: inherit; + letter-spacing: inherit; + line-height: inherit; +} + +[data-component="oc-text-shimmer"] [data-slot="text-shimmer-char-base"], +[data-component="oc-text-shimmer"] [data-slot="text-shimmer-char-shimmer"] { + grid-area: 1 / 1; + white-space: inherit; + font: inherit; + letter-spacing: inherit; + line-height: inherit; +} + +[data-component="oc-text-shimmer"] [data-slot="text-shimmer-char-base"] { + color: inherit; + opacity: 0; +} + +[data-component="oc-text-shimmer"] [data-slot="text-shimmer-char-shimmer"] { + color: var(--surface-muted-foreground); + opacity: 1; +} + +[data-component="oc-text-shimmer"] [data-slot="text-shimmer-char-shimmer"][data-run="true"] { + animation-name: oc-text-shimmer-sweep; + animation-duration: var(--oc-text-shimmer-duration); + animation-iteration-count: infinite; + animation-timing-function: linear; + animation-fill-mode: both; + animation-delay: calc(var(--oc-text-shimmer-step) * -1); + will-change: background-position; +} + +@keyframes oc-text-shimmer-sweep { + 0% { + background-position: + 100% 0, + 0 0; + } + + 100% { + background-position: + 0% 0, + 0 0; + } +} + +@supports ((-webkit-background-clip: text) or (background-clip: text)) { + [data-component="oc-text-shimmer"] [data-slot="text-shimmer-char-shimmer"] { + color: transparent; + -webkit-text-fill-color: transparent; + background-image: var(--oc-text-shimmer-sweep), var(--oc-text-shimmer-base); + background-size: + var(--oc-text-shimmer-size) 100%, + 100% 100%; + background-position: + 100% 0, + 0 0; + background-repeat: no-repeat; + -webkit-background-clip: text; + background-clip: text; + } +} + +@media (prefers-reduced-motion: reduce) { + [data-component="oc-text-shimmer"] [data-slot="text-shimmer-char-shimmer"] { + animation: none !important; + color: inherit; + -webkit-text-fill-color: currentColor; + background-image: none; + } +} + @keyframes gradient-shimmer { 0% { stop-color: currentColor;