Files
fizzy/app/assets/stylesheets/card-columns.css
T
Andy Smith e2f3d70bfe Merge branch 'main' into update-column-icons
* main: (41 commits)
  Remove animations from popups
  Remove fixed position
  Sticky Fizzy nav
  Account for MS mail
  Style avatar initials
  Save three requests per card view
  Ensure mention avatars cover the entire circle
  Bump fizzy
  Enable public file server for assets
  Respect size
  Reuse logo as avatar
  Add test
  Add background color
  Don't render SVG avatars in emails since mail services won't render them
  Disable user creation tests for the OSS config
  Update test
  Remove debug
  Further copy tweaks
  More copy and design edits
  Extract saas helpers into a concern
  ...
2025-11-07 15:59:09 -06:00

732 lines
17 KiB
CSS

@layer components {
/* Column container
/* ------------------------------------------------------------------------ */
#main:has(.card-columns) {
--main-padding: 0;
}
.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;
-ms-overflow-style: none; /* Hide x-scrollbar on Edge */
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;
padding-block-end: var(--column-width-collapsed);
position: relative;
scrollbar-width: none; /* Hide x-scrollbar on FF */
/* Hide x-scrollbar on Chrome/Safari/Opera */
&::-webkit-scrollbar {
display: none;
}
/* 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;
padding-inline-start: var(--column-gap);
}
.card-columns__right {
justify-content: start;
padding-inline-end: var(--column-gap);
}
/* 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;
}
}
/* 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__header {
.cards.is-collapsed & {
block-size: 100%;
}
.cards:not(.is-collapsed) & {
display: grid;
grid-template-areas: "menu expander maximize";
grid-template-columns: auto 1fr auto;
margin-block-end: calc(0.5 * var(--cards-gap));
}
}
.cards__menu .btn--circle,
.cards__maximize-button {
--btn-background: transparent;
block-size: var(--column-width-collapsed);
inline-size: var(--column-width-collapsed);
opacity: 0;
outline-offset: -2px;
.cards:hover &,
&:focus-visible {
opacity: 1;
}
.cards.is-collapsed & {
display: none;
}
}
.cards__menu {
z-index: var(--z-popup);
}
.cards__maximize-button {
grid-area: maximize;
}
.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;
grid-area: expander;
outline-offset: -2px;
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%;
justify-content: center;
}
.cards:is(.cards--considering) &:hover {
cursor: unset;
filter: 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) & {
align-items: center;
display: flex;
gap: 0.25ch;
max-inline-size: calc(100% - var(--column-width-collapsed) * 2);
}
.icon--collapse {
--icon-size: 1.15em;
opacity: 0;
scale: 1.5;
transition: 150ms ease-out;
transition-property: opacity, scale;
.cards.is-collapsed & {
display: none;
}
.cards:not(.is-collapsed) .cards__expander:hover & {
opacity: 0.66;
scale: 1;
}
}
}
.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);
}
}
}
}