Something went wrong. Try again.
a reactive (signals based) hypermedia web framework (wip) stormlightlabs.github.io/volt
hypermedia frontend signals
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301/* ========================================================================== *//* Components *//* ========================================================================== */
/** * Dialog (Modal) * * Native <dialog> element provides semantic modal functionality. * Structure: <dialog><article><header>...</header>content<footer>...</footer></article></dialog> * Close buttons should use aria-label="Close" for targeting. */dialog { border: none; padding: 0; margin: auto; max-width: min(90vw, 600px); max-height: 90vh; background-color: var(--color-bg); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); overflow: hidden;}
dialog::backdrop { background-color: rgba(0, 0, 0, 0.5); backdrop-filter: blur(4px);}
@media (prefers-color-scheme: dark) { dialog::backdrop { background-color: rgba(0, 0, 0, 0.7); }}
dialog article { margin: 0; padding: 0; display: flex; flex-direction: column; max-height: 90vh;}
dialog header { position: relative; border-bottom: 1px solid var(--color-border); padding: var(--space-lg); margin: 0;}
dialog header h1,dialog header h2,dialog header h3,dialog header h4,dialog header h5,dialog header h6 { margin-top: 0; margin-bottom: 0; padding-right: var(--space-3xl);}
dialog header button[aria-label="Close"],dialog header button[aria-label="close"] { position: absolute; top: var(--space-lg); right: var(--space-lg); background: none; border: none; font-size: 1.75rem; line-height: 1; padding: var(--space-xs); margin: 0; width: auto; color: var(--color-text-muted); cursor: pointer; transition: color var(--transition-fast), transform var(--transition-fast);}
dialog header button[aria-label="Close"]:hover,dialog header button[aria-label="close"]:hover { color: var(--color-text); background: none; transform: scale(1.1);}
dialog article > :not(header):not(footer) { padding: var(--space-lg); overflow-y: auto; flex: 1;}
dialog form { margin: 0; max-width: none;}
dialog footer { border-top: 1px solid var(--color-border); padding: var(--space-lg); margin: 0; display: flex; gap: var(--space-md); justify-content: flex-end; flex-wrap: wrap;}
dialog footer button { margin: 0;}
@media (max-width: 768px) { dialog { max-width: 95vw; max-height: 95vh; }
dialog header, dialog article > :not(header):not(footer), dialog footer { padding: var(--space-md); }
dialog header button[aria-label="Close"], dialog header button[aria-label="close"] { top: var(--space-md); right: var(--space-md); }}
/** * Accordian * * implemented as details and summary */details { margin: var(--space-lg) 0; padding: var(--space-md); border: 1px solid var(--color-border); border-radius: var(--radius-md); max-width: var(--content-width);}
summary { font-weight: 700; cursor: pointer; user-select: none; padding: var(--space-sm); margin: calc(-1 * var(--space-sm)); transition: background-color var(--transition-fast);}
summary:hover { background-color: var(--color-bg-alt);}
details[open] summary { margin-bottom: var(--space-md); border-bottom: 1px solid var(--color-border);}
/** * Tooltips * * Declarative tooltips using data-vx-tooltip attribute. * Placements: top (default), right, bottom, left * Structure: <element data-vx-tooltip="Tooltip text" data-placement="top"> */[data-vx-tooltip] { position: relative; cursor: help;}
[data-vx-tooltip]::before,[data-vx-tooltip]::after { position: absolute; opacity: 0; pointer-events: none; transition: opacity var(--transition-fast), transform var(--transition-fast); z-index: 1000;}
[data-vx-tooltip]::before { content: attr(data-vx-tooltip); background: var(--color-text); color: var(--color-bg); padding: var(--space-sm) var(--space-md); border-radius: var(--radius-sm); font-size: 0.875rem; white-space: normal; min-width: 200px; max-width: 300px; width: max-content; text-align: center; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4; line-clamp: 4; overflow: hidden; text-overflow: ellipsis; line-height: 1.4;}
[data-vx-tooltip]::after { content: ''; border: 6px solid transparent;}
[data-vx-tooltip]:hover::before,[data-vx-tooltip]:hover::after,[data-vx-tooltip]:focus::before,[data-vx-tooltip]:focus::after,[data-vx-tooltip]:focus-visible::before,[data-vx-tooltip]:focus-visible::after { opacity: 1;}
/* Placement: Top (default) */[data-vx-tooltip]:not([data-placement])::before,[data-vx-tooltip][data-placement="top"]::before { bottom: calc(100% + 12px); left: 50%; transform: translateX(-50%) translateY(4px);}
[data-vx-tooltip]:not([data-placement])::after,[data-vx-tooltip][data-placement="top"]::after { bottom: calc(100% + 6px); left: 50%; transform: translateX(-50%); border-top-color: var(--color-text);}
[data-vx-tooltip]:not([data-placement]):hover::before,[data-vx-tooltip][data-placement="top"]:hover::before,[data-vx-tooltip]:not([data-placement]):focus::before,[data-vx-tooltip][data-placement="top"]:focus::before { transform: translateX(-50%) translateY(0);}
/* Placement: Bottom */[data-vx-tooltip][data-placement="bottom"]::before { top: calc(100% + 12px); left: 50%; transform: translateX(-50%) translateY(-4px);}
[data-vx-tooltip][data-placement="bottom"]::after { top: calc(100% + 6px); left: 50%; transform: translateX(-50%); border-bottom-color: var(--color-text);}
[data-vx-tooltip][data-placement="bottom"]:hover::before,[data-vx-tooltip][data-placement="bottom"]:focus::before { transform: translateX(-50%) translateY(0);}
[data-vx-tooltip][data-placement="right"]::before { left: calc(100% + 12px); top: 50%; transform: translateY(-50%) translateX(-4px);}
[data-vx-tooltip][data-placement="right"]::after { left: calc(100% + 6px); top: 50%; transform: translateY(-50%); border-right-color: var(--color-text);}
[data-vx-tooltip][data-placement="right"]:hover::before,[data-vx-tooltip][data-placement="right"]:focus::before { transform: translateY(-50%) translateX(0);}
[data-vx-tooltip][data-placement="left"]::before { right: calc(100% + 12px); top: 50%; transform: translateY(-50%) translateX(4px);}
[data-vx-tooltip][data-placement="left"]::after { right: calc(100% + 6px); top: 50%; transform: translateY(-50%); border-left-color: var(--color-text);}
[data-vx-tooltip][data-placement="left"]:hover::before,[data-vx-tooltip][data-placement="left"]:focus::before { transform: translateY(-50%) translateX(0);}
@media (max-width: 768px) { [data-vx-tooltip]::before, [data-vx-tooltip]::after { display: none; }}