Files
supabase__supabase/packages/config/css/animations.css
Anthony Lio 38f448b01c fix(ui): tab component (#50183)
## What kind of change does this PR introduce?
bug fix on tab component + a small refactor

## What is the current behavior?

the active tab in an underline list gets border-b-2 while its siblings
get nothing, so it's 2px taller and its label sits higher than the rest
causing a smol layout shift within docs

## What is the new behavior?

- adds one absolute positioned bar that slides between tabs so nothing
moves
- favors track under an underline as an inset shadow vs a border

| state | preview |
| -------|------|
| before | <video
src="https://github.com/user-attachments/assets/b73820a6-2994-46d1-aa98-452681f0fef2"
/> |
| after | <video
src="https://github.com/user-attachments/assets/054cd67d-a50c-4aef-9340-a1e1d515047b"
/> |


## Test
1. visit [api
reference](https://docs-git-antlio-ui-components-tabs-supabase.vercel.app/docs/reference/javascript/installing?platform=npm&queryGroups=platform)
2. visit a [guide
](https://docs-git-antlio-ui-components-tabs-supabase.vercel.app/docs/guides/database/prisma)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Added an animated tab indicator that follows the active tab and adapts
to layout changes.
  - Added support for customizing tab indicator styling.
- Respects reduced-motion preferences by disabling indicator transitions
when appropriate.

- **Style**
  - Streamlined tab borders, spacing, and underlined-tab styling.
- Improved tab panel spacing and standardized tab behavior in
documentation examples.
- Centralized easing behavior for smoother overlays, dropdowns, slides,
and panels.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Danny White <3104761+dnywh@users.noreply.github.com>
2026-09-16 00:00:27 +03:00

311 lines
5.8 KiB
CSS

/*
* Tailwind v4 animation tokens + the @keyframes they reference.
*
* Each --animate-* token in @theme generates an `animate-{name}` utility.
* Keyframes live outside @theme since they're @-rules, not design tokens.
*
* Migrated from packages/config/ui.config.js (theme.extend.keyframes /
* theme.extend.animation) and packages/config/tailwind.config.js
* (theme.extend.keyframes / theme.extend.animation).
*/
@theme {
--ease-enter: cubic-bezier(0.16, 1, 0.3, 1);
--ease-move: cubic-bezier(0.87, 0, 0.13, 1);
--animate-fade-in: fadeIn 300ms both;
--animate-fade-out: fadeOut 300ms both;
/* may be unused — verify before pruning */
--animate-dropdown-content-show: overlayContentShow 100ms var(--ease-enter);
/* may be unused — verify before pruning */
--animate-dropdown-content-hide: overlayContentHide 100ms var(--ease-enter);
--animate-overlay-show: overlayContentShow 300ms var(--ease-enter);
--animate-overlay-hide: overlayContentHide 300ms var(--ease-enter);
/* may be unused — verify before pruning */
--animate-fade-in-overlay-bg: fadeInOverlayBg 300ms;
/* may be unused — verify before pruning */
--animate-fade-out-overlay-bg: fadeOutOverlayBg 300ms;
--animate-slide-down: slideDown 300ms var(--ease-move);
--animate-slide-up: slideUp 300ms var(--ease-move);
--animate-slide-down-normal: slideDownNormal 300ms var(--ease-move);
--animate-slide-up-normal: slideUpNormal 300ms var(--ease-move);
/* may be unused — verify before pruning */
--animate-panel-slide-left-out: panelSlideLeftOut 200ms var(--ease-move);
/* may be unused — verify before pruning */
--animate-panel-slide-left-in: panelSlideLeftIn 250ms var(--ease-move);
/* may be unused — verify before pruning */
--animate-panel-slide-right-out: panelSlideRightOut 200ms var(--ease-move);
/* may be unused — verify before pruning */
--animate-panel-slide-right-in: panelSlideRightIn 250ms var(--ease-move);
--animate-line-loading: lineLoading 1.8s infinite;
--animate-line-loading-slower: lineLoading 2.3s infinite;
/* may be unused — verify before pruning */
--animate-dropdownFadeIn: dropdownFadeIn 0.1s ease-out;
/* may be unused — verify before pruning */
--animate-dropdownFadeOut: dropdownFadeOut 0.1s ease-out;
--animate-flash-code: flash-code 1s forwards;
--animate-flash-code-slow: flash-code 2s forwards;
--animate-accordion-down: accordion-down 0.15s ease-out;
--animate-accordion-up: accordion-up 0.15s ease-out;
--animate-collapsible-down: collapsible-down 0.1s ease-out;
--animate-collapsible-up: collapsible-up 0.1s ease-out;
}
@keyframes fadeIn {
0% {
transform: scale(0.95);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 1;
}
}
@keyframes fadeOut {
0% {
transform: scale(1);
opacity: 1;
}
100% {
transform: scale(0.95);
opacity: 0;
}
}
@keyframes overlayContentShow {
0% {
opacity: 0;
transform: translate(0%, -2%) scale(1);
}
100% {
opacity: 1;
transform: translate(0%, 0%) scale(1);
}
}
@keyframes overlayContentHide {
0% {
opacity: 1;
transform: translate(0%, 0%) scale(1);
}
100% {
opacity: 0;
transform: translate(0%, -2%) scale(1);
}
}
@keyframes dropdownFadeIn {
0% {
transform: scale(0.95);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 1;
}
}
@keyframes dropdownFadeOut {
0% {
transform: scale(1);
opacity: 1;
}
100% {
transform: scale(0.95);
opacity: 0;
}
}
@keyframes fadeInOverlayBg {
0% {
opacity: 0;
}
100% {
opacity: 0.75;
}
}
@keyframes fadeOutOverlayBg {
0% {
opacity: 0.75;
}
100% {
opacity: 0;
}
}
@keyframes slideDown {
0% {
height: 0;
opacity: 0;
}
100% {
height: var(--radix-accordion-content-height, var(--radix-collapsible-content-height));
opacity: 1;
}
}
@keyframes slideUp {
0% {
height: var(--radix-accordion-content-height, var(--radix-collapsible-content-height));
opacity: 1;
}
100% {
height: 0;
opacity: 0;
}
}
@keyframes slideDownNormal {
0% {
height: 0;
opacity: 0;
}
100% {
height: inherit;
opacity: 1;
}
}
@keyframes slideUpNormal {
0% {
height: inherit;
opacity: 1;
}
100% {
height: 0;
opacity: 0;
}
}
@keyframes panelSlideLeftOut {
0% {
transform: translateX(-100%);
opacity: 0;
}
100% {
transform: translateX(0);
opacity: 1;
}
}
@keyframes panelSlideLeftIn {
0% {
transform: translateX(0);
opacity: 1;
}
100% {
transform: translateX(-100%);
opacity: 0;
}
}
@keyframes panelSlideRightOut {
0% {
transform: translateX(100%);
opacity: 0;
}
100% {
transform: translateX(0);
opacity: 1;
}
}
@keyframes panelSlideRightIn {
0% {
transform: translateX(0);
opacity: 1;
}
100% {
transform: translateX(100%);
opacity: 0;
}
}
@keyframes lineLoading {
0% {
margin-left: -10%;
width: 80px;
}
25% {
width: 240px;
}
50% {
margin-left: 100%;
width: 80px;
}
75% {
width: 240px;
}
100% {
margin-left: -10%;
width: 80px;
}
}
@keyframes flash-code {
0% {
background-color: rgba(63, 207, 142, 0.1);
}
100% {
background-color: transparent;
}
}
@keyframes accordion-down {
from {
height: 0;
}
to {
height: var(--radix-accordion-content-height);
}
}
@keyframes accordion-up {
from {
height: var(--radix-accordion-content-height);
}
to {
height: 0;
}
}
@keyframes collapsible-down {
from {
height: 0;
}
to {
height: var(--radix-collapsible-content-height);
}
}
@keyframes collapsible-up {
from {
height: var(--radix-collapsible-content-height);
}
to {
height: 0;
}
}
@keyframes tw-shimmer {
from {
background-position: 100% 0;
}
to {
background-position: 0 0;
}
}