defuss-shadcn / Website / Company / project-item
Built with: CSS
Project ItemBLK
Previews one portfolio project with imagery and context - picture first, caption under it or revealed over it, or a wide feature. Part of the Company blocks.
On this page (4)
§Grid
Default: the picture (it zooms a little on hover), then the title link - covering the card - category, year and up to three tags.
§Overlay
data-variant='overlay': the caption slides up over a 4:5 picture on hover - or when the link has keyboard focus (:focus-within); on touch screens it is always shown.
§Wide
data-variant='wide': a 21:9 picture with the title and its meta on one line - one featured project.
§CSS view file
/* -- Project Item (website · Company) ------------------------------------- Previews one portfolio project with imagery and context - picture first, caption under it or revealed over it, or a wide feature. */@layer components { /* every box in the block measures border-box - the same on any page */ :where(.mk-project-item, .mk-project-item *, .mk-project-item ::before, .mk-project-item ::after) { box-sizing: border-box; } .mk-project-item { position: relative; display: flex; flex-direction: column; gap: 0.875rem; } .mk-project-item { position: relative; } .mk-project-item-title a::after { content: ""; position: absolute; inset: 0; border-radius: inherit; } .mk-project-item-title a:focus-visible { outline: none; } .mk-project-item:has(.mk-project-item-title a:focus-visible) { outline: 2px solid var(--ring); outline-offset: 2px; } /* zero specificity: any block rule may still position its controls */ :where(.mk-project-item :is(button, input, select, label, .btn):not(.mk-project-item-title a)) { position: relative; z-index: 1; } .mk-project-item:has(.mk-project-item-title a:focus-visible) { border-radius: var(--radius-xl); } .mk-project-item-media { margin: 0; overflow: hidden; border-radius: var(--radius-xl); background: var(--muted); & img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; transition: scale 500ms cubic-bezier(.2,.7,.2,1); } } .mk-project-item:hover .mk-project-item-media img { scale: 1.05; } .mk-project-item-body { display: grid; gap: 0.25rem; } .mk-project-item-title { margin: 0; font-size: 1.125rem; font-weight: 600; letter-spacing: -0.01em; & a { color: inherit; text-decoration: none; } } .mk-project-item-meta { display: flex; gap: 0.5rem; margin: 0; color: var(--muted-foreground); font-size: 0.875rem; & span + span::before { content: "·"; margin-inline-end: 0.5rem; } } .mk-project-item-tags { display: flex; flex-wrap: wrap; gap: 0.375rem; margin: 0.375rem 0 0; padding: 0; list-style: none; & li { padding: 0.125rem 0.5rem; border: 1px solid var(--border); border-radius: 999px; color: var(--muted-foreground); font-size: 0.75rem; } } .mk-project-item[data-variant="overlay"] { overflow: hidden; border-radius: var(--radius-xl); & .mk-project-item-media img { aspect-ratio: 4 / 5; } & .mk-project-item-body { position: absolute; inset: auto 0 0; padding: 3rem 1.25rem 1.25rem; background: linear-gradient(transparent, oklch(0.12 0.02 260 / 0.85)); color: #fff; opacity: 0; translate: 0 1rem; transition: opacity 250ms ease, translate 250ms ease; } & .mk-project-item-meta { color: oklch(1 0 0 / 0.8); } &:is(:hover, :focus-within) .mk-project-item-body { opacity: 1; translate: 0 0; } } @media (hover: none) { .mk-project-item[data-variant="overlay"] .mk-project-item-body { opacity: 1; translate: 0 0; } } .mk-project-item[data-variant="wide"] { & .mk-project-item-media img { aspect-ratio: 21 / 9; } & .mk-project-item-body { grid-template-columns: minmax(0, 1fr) auto; align-items: baseline; } & .mk-project-item-title { font-size: 1.5rem; } } /* -- Accessibility ------------------------------------------- */ @media (prefers-reduced-motion: reduce) { .mk-project-item-media img, .mk-project-item[data-variant="overlay"] .mk-project-item-body { transition: none; } .mk-project-item:hover .mk-project-item-media img { scale: none; } } @media (forced-colors: active) { .mk-project-item[data-variant="overlay"] .mk-project-item-body { background: Canvas; color: CanvasText; } }}Comments, ideas or improvements? Edit this page's source on GitHub