Files
fizzy/app/assets/stylesheets/card-columns.css
T
Andy Smith 8280be59c5 Merge branch 'main' into sticky-column-headers-using-fixed
* main:
  Order of concerns
  Redirect to root to avoid changing the path by default
  Make sure that only admins or card creators can delete cards
  Make sure only admins or collection creators can toggle the individual access settings
  Make sure only admins or collection creators can toggle the all access setting
  Update seeds from "Collection" to "Board"
  Rename concern
  Fix: constant name
  Rename script I did not mean to commit
  Support old public collection urls
  Support old collection urls
  Support old events in the timeline
  Invalidate HTTP cache
  Support current events after renaming
  Replace collection with board in the code, tests green
  Baseline replacing collection with board across code
2025-11-05 12:40:19 -06:00

717 lines
16 KiB
CSS

@layer components {
/* Column container
/* ------------------------------------------------------------------------ */
#main:has(.card-columns) {
--bubble-size: 3.5rem;
--cards-gap: min(1.2cqi, 1.7rem);
--column-gap: 8px;
--column-padding-expanded: calc(var(--column-gap) * 2);
--column-transition-duration: 300ms;
--column-width-collapsed: 40px;
--column-width-expanded: 450px;
--progress-increment: var(--progress-max-height) / var(--progress-max-cards);
--progress-max-cards: 20;
--progress-max-height: 50dvh;
padding-inline: var(--column-gap);
}
.card-columns {
container-type: inline-size;
display: grid;
gap: var(--column-gap);
grid-template-columns: 1fr var(--column-width-expanded) 1fr;
margin-inline: auto;
max-inline-size: var(--main-width);
overflow-x: auto;
overflow-y: hidden;
position: relative;
/* When it has something expanded */
&:has(.card-columns__left .cards:not(.is-collapsed), .card-columns__right .cards:not(.is-collapsed)) {
grid-template-columns: auto var(--column-width-expanded) auto;
}
&:has(.cards) {
min-block-size: 20lh;
}
@media (max-width: 639px) {
--bubble-size: 3rem;
}
}
.card-columns__left,
.card-columns__right {
align-items: stretch;
display: flex;
gap: var(--column-gap);
}
.card-columns__left {
justify-content: end;
}
.card-columns__right {
justify-content: start;
}
/* Column
/* ------------------------------------------------------------------------ */
.cards {
--column-color: color-mix(in srgb, var(--card-color) 15%, var(--color-canvas));
inline-size: var(--column-width-expanded);
position: relative;
scroll-snap-align: start;
&.is-collapsed {
inline-size: var(--column-width-collapsed);
.pagination-link.pagination-link--active-when-observed,
.card {
display: none;
}
}
&.drag-and-drop__hover-container {
--dnd-bg-color: transparent;
--dnd-border-color: transparent;
&.is-off-screen {
&:after {
content: attr(data-column-name);
font-size: var(--text-x-small);
font-weight: 500;
line-height: var(--column-width-collapsed);
padding-inline: 1ch;
position: fixed;
text-transform: uppercase;
top: 0;
translate: -50%;
}
&.is-collapsed {
&:after {
writing-mode: vertical-rl;
}
}
&:not(.is-collapsed) {
&:after {
background-color: var(--column-color);
inline-size: calc(var(--column-width-expanded) - 4px); /* make room for the dnd border */
}
}
}
}
@media (any-hover: hover) {
.card:has(.card__background img:not([src=""])):hover .card__background img:not([src=""]) {
filter: blur(3px) brightness(1.2);
opacity: 0.2;
}
}
}
.cards__transition-container {
block-size: 100%;
border-radius: calc(var(--column-width-collapsed) / 2);
translate: 0;
transition: translate var(--column-transition-duration) var(--ease-out-overshoot-subtle);
.is-collapsed:not(.cards--considering) & {
translate: 0 var(--column-width-collapsed);
}
.cards:not(.is-collapsed) & {
padding-inline: var(--column-padding-expanded);
}
.drag-and-drop__hover-container & {
--dnd-bg-color: var(--column-color);
--dnd-border-color: var(--card-color);
background-color: var(--dnd-bg-color);
outline: 2px dashed var(--dnd-border-color);
outline-offset: -2px;
transition: background-color 200ms;
z-index: 1;
}
.no-transitions & {
transition: none;
}
}
.cards__header {
.cards.is-collapsed & {
block-size: 100%;
}
}
/* The wrapper around the cards used to clip overflow while transitioning.
* Also, don't resize cards while transitioning to avoid reflow. */
.cards__list {
align-items: flex-end; /* use flex-start to wipe from left */
display: flex;
flex-direction: column;
gap: var(--cards-gap);
margin-block-start: -1ch;
margin-inline: -1ch;
overflow: hidden;
.cards:not(.is-collapsed) & {
padding: 1ch;
}
.card {
inline-size: calc(var(--column-width-expanded) - var(--column-padding-expanded) * 2);
}
.cards--grid & {
display: contents;
}
}
.cards__new-column {
margin-block-start: var(--column-width-collapsed);
}
/* Cards grid; used when filtering
/* -------------------------------------------------------------------------- */
.cards--grid {
--cards-gap: 1rem;
--card-grid-columns: 1;
container-type: inline-size;
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: var(--cards-gap);
justify-content: center;
margin-inline: auto;
max-inline-size: var(--main-width);
@media (min-width: 640px) {
--card-grid-columns: 2;
}
@media (min-width: 960px) {
--card-grid-columns: 3;
}
.card {
inline-size: calc((100% - var(--cards-gap) * (var(--card-grid-columns) - 1) ) / var(--card-grid-columns));
}
.card__header .card__column-name--current {
--btn-padding: 0.1em 0.5em;
background: none;
border: 1px solid currentColor;
color: var(--card-color);
display: inline-flex;
flex: 0 1 auto;
inline-size: fit-content;
margin: 0 0 0 auto;
}
}
/* Column Elements
/* ------------------------------------------------------------------------ */
.cards__expander {
align-items: center;
border-radius: 99rem;
cursor: pointer;
display: flex;
flex-direction: row-reverse;
font-size: var(--text-x-small);
font-weight: 600;
gap: 0.5ch;
position: relative;
text-transform: uppercase;
@media (any-hover: hover) {
&:hover {
filter: brightness(0.9);
}
}
/* Progress */
&:after {
background: linear-gradient(to bottom, var(--card-color), var(--column-color) 80%);
block-size: var(--column-width-collapsed);
border-radius: 99rem;
content: "";
inset: 0 0 auto;
margin-inline: auto;
max-block-size: var(--progress-max-height);
min-block-size: var(--column-width-collapsed);
opacity: 0;
position: absolute;
transition:
block-size 500ms var(--ease-out-overshoot),
inline-size var(--column-transition-duration) ease-out,
opacity var(--column-transition-duration) ease-out;
z-index: -1;
}
.no-transitions &:after {
transition: none;
}
.cards.is-collapsed & {
block-size: 100%;
flex-direction: column;
inline-size: var(--column-width-collapsed);
justify-content: start;
letter-spacing: 0.05em;
/* Guitar string */
&:before {
background-color: var(--column-color);
block-size: 100%;
content: "";
inline-size: 1px;
inset: 0 auto;
position: absolute;
z-index: -2;
}
&:after {
block-size: calc(var(--column-width-collapsed) + var(--card-count) * var(--progress-increment));
opacity: 1;
inline-size: var(--column-width-collapsed);
}
}
.cards:not(.is-collapsed) & {
inline-size: 100%;
margin-block-end: calc(0.5 * var(--cards-gap));
justify-content: center;
}
.cards:is(.cards--considering) &:hover {
cursor: unset;
filter: none;
}
@media (any-hover: hover) {
.btn--circle,
~ .btn--circle,
~ .cards__expander-menu .btn--circle:not(label) {
transition: opacity 300ms ease-in-out;
opacity: 0;
&:focus-visible,
.cards:hover & {
opacity: 1;
}
}
}
}
.btn.cards__expander-button {
--btn-background: transparent;
block-size: var(--column-width-collapsed);
inline-size: var(--column-width-collapsed);
inset: 0 0 auto auto;
position: absolute;
translate: calc(var(--column-gap) * -2);
z-index: var(--z-popup);
.cards--considering & {
translate: none;
}
.cards.is-collapsed & {
display: none;
}
}
.cards__expander-count {
line-height: var(--column-width-collapsed);
inline-size: var(--column-width-collapsed);
.cards:not(.is-collapsed) & {
display: none;
}
}
.cards__expander-title {
font-weight: inherit;
font-size: inherit;
line-height: var(--column-width-collapsed);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.cards.is-collapsed & {
max-inline-size: 50vh;
writing-mode: vertical-rl;
}
.cards:not(.is-collapsed, .cards--considering) & {
max-inline-size: calc(100% - var(--column-width-collapsed) * 2);
}
}
.cards__expander-menu {
inset: 0 auto auto 0;
position: absolute;
translate: calc(var(--column-gap) * 2);
z-index: var(--z-popup);
.btn--circle {
--btn-background: transparent;
block-size: var(--column-width-collapsed);
inline-size: var(--column-width-collapsed);
}
.cards.is-collapsed & {
display: none;
}
}
.blank-slate--card {
box-shadow: none !important;
color: color-mix(in srgb, var(--card-color) 70%, var(--color-canvas));
p {
font-size: var(--text-small);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
&.blank-slate-for-grid {
display: none;
.cards--grid & {
display: initial;
}
}
.cards--grid &:not(.blank-slate-for-grid) {
display: none;
}
}
}
/* TODO: I think this is legacy now? */
.cards__heading {
display: flex;
font-size: var(--text-medium);
inset-block-start: 0;
justify-content: center;
margin-block-start: 1px;
margin-inline: -8px; /* enough to cover card shadows, but avoid overlapping bubbles */
padding-block: var(--cards-gap) 0;
position: sticky;
z-index: 3;
/* Cover bubbles and cards on small viewports */
@media (max-width: 639px) {
&:not(.cards--closed &) {
margin-inline: calc(-1 * var(--cards-gap) - var(--main-padding));
}
}
&, .cards__filter {
font-weight: bold;
text-transform: uppercase;
}
.cards__filter {
--filter-block-padding: 0.5rem;
background-color: var(--color-canvas);
border-radius: 99rem;
inline-size: auto;
margin-block: calc(-1 * var(--filter-block-padding));
padding-block: var(--filter-block-padding);
}
dialog {
font-weight: normal;
text-transform: none;
}
}
/* Override card styles within columns
/* ------------------------------------------------------------------------ */
.cards {
.card {
--block-space: 1em;
--block-space-half: 0.5em;
--card-padding-inline: 1em;
--text-xx-large: 1.6em;
--text-x-small: 1em;
/* Set lower limit for font size */
font-size: clamp(0.6rem, 0.85cqi, 100px);
}
.card__steps {
display: flex;
}
.card__tags {
gap: calc(var(--card-header-space) / 2);
}
.card__title {
line-height: 1.2;
pointer-events: none;
}
.card__link {
z-index: 1;
}
.card__stages,
.card__hide-on-index {
display: none;
}
.card__body {
padding-block: calc(var(--card-padding-block) * 0.75) var(--card-padding-block);
}
.card__meta {
font-weight: 600;
strong,
.local-time-value {
font-weight: inherit;
}
}
.card:has(.card__background img:not([src=""])) {
.card__content,
.card__meta,
.card__column-name:not(.card__column-name--current) {
opacity: 0;
transition: opacity 0.2s ease-in-out;
@media (any-hover: hover) {
.card:hover & {
opacity: 1;
}
}
}
}
.bubble {
inset-inline-start: 100%;
translate: -90% -40%;
}
@media (any-hover: hover) {
.card:has(.card__background img:not([src=""])):hover .card__background img:not([src=""]) {
filter: blur(3px) brightness(1.2);
opacity: 0.2;
}
}
}
/* Considering
/* ------------------------------------------------------------------------ */
.cards--considering {
--card-color: oklch(var(--lch-blue-medium));
position: relative;
.card {
--avatar-size: 2.75em;
--text-small: 1.1em;
background-color: var(--color-canvas);
line-height: 1.2;
z-index: 2;
@media (min-width: 640px) {
--text-xx-large: 1.6em;
}
}
.card__board {
background-color: transparent;
color: var(--card-content-color);
}
.card__header {
color: var(--color-ink);
padding-block-start: calc(var(--card-padding-block) / 2);
}
.card__tags {
color: inherit;
}
.card__body {
padding-block: 0 var(--card-padding-block);
}
.card__people-label {
display: none;
}
.card__title {
min-block-size: 0;
}
}
/* Board tools
/* -------------------------------------------------------------------------- */
.board-tools.card {
--border-color: var(--color-selected-dark);
--border-size: 1px;
--card-padding-block: var(--block-space);
border: 1px solid var(--border-color);
margin-block-end: var(--cards-gap);
text-align: center;
&:has(dialog[open]) {
z-index: 5;
}
.separator--horizontal {
margin-block: var(--block-space);
}
.btn--link {
font-size: 1.2em;
}
.btn:not(.btn--link, .btn--circle) {
border: 0;
color: var(--color-link);
}
footer {
font-size: var(--text-x-small);
margin-block-end: calc(var(--block-space-half) * -1);
}
.overflow-count {
font-size: 1.2em;
font-weight: 500;
padding: 0.5em 0.3em;
}
}
.board-tools__watching {
--btn-size: 32px;
--gap: 0.5ch;
display: flex;
gap: var(--gap);
inline-size: 100%;
margin-block: var(--block-space-half);
place-content: center;
position: relative;
}
.board-tools__watching-dialog {
--panel-padding: 2ch;
--panel-size: 100%;
display: flex;
flex-wrap: wrap;
gap: var(--gap);
inset-block-start: 0;
justify-content: center;
position: absolute;
z-index: 1;
}
/* On Deck (Not Now)
/* ------------------------------------------------------------------------ */
.cards--on-deck {
--card-color: var(--color-ink-light) !important;
.card {
--card-color: var(--color-card-complete) !important;
}
.bubble {
display: none !important;
}
}
/* Doing
/* -------------------------------------------------------------------------- */
/* Surface a mini bubble if there are cards with bubbles inside */
.cards--considering:has(.bubble:not([hidden])) .cards__expander-title,
.cards--doing.is-collapsed:has(.bubble:not([hidden])) {
--bubble-color: var(--card-color, oklch(var(--lch-blue-medium)));
--bubble-shape: 54% 46% 61% 39% / 57% 49% 51% 43%;
&:before {
background: radial-gradient(
color-mix(in srgb, var(--bubble-color) 8%, var(--color-canvas)) 50%,
color-mix(in srgb, var(--bubble-color) 48%, var(--color-canvas)) 100%
);
block-size: 1em;
border-radius: var(--bubble-shape);
content: "";
inline-size: 1em;
inset: 0 0 auto auto;
position: absolute;
translate: 25% -25%;
z-index: 1;
}
}
.cards--considering:has(.bubble:not([hidden])) .cards__expander-title:before {
translate: 120% 50%;
}
/* Card column indicators
/* -------------------------------------------------------------------------- */
.card__column-name {
--btn-background: transparent;
--btn-padding: 0.2em 0.5em;
--btn-border-size: 0;
--btn-border-radius: 0.2em;
color: inherit;
inline-size: 100%;
justify-content: flex-start;
text-transform: uppercase;
@media (hover: hover) {
&:hover {
--btn-background: color(from var(--card-color) srgb r g b / 0.15);
}
}
}
.card__column-name--current {
--btn-background: var(--card-color);
color: var(--color-ink-inverted);
@media (hover: hover) {
&:hover {
--btn-background: var(--card-color);
}
}
}
}