Contents
Smol Responsive CSS Grid
Create an intrinsically responsive grid layout, optionally using a CSS custom property to extend to varable contexts. Each column will resize at the same rate, and break to a new row if the width reaches the --min
value.
CSS for “Smol Responsive CSS Grid”
.smol-css-grid {
--min: 15ch;
--gap: 1rem;display: grid;
grid-gap: var(--gap);
grid-template-columns: repeat(auto-fit, minmax(var(--min), 1fr));
}
Smol Responsive Flexbox Grid
Create an intrinsically responsive grid layout, optionally using a CSS custom property to extend to varable contexts. Each column will resize at the same rate until reaching the --min
width. At that point, the last item will break to a new row and fill any available space.
CSS for “Smol Responsive Flexbox Grid”
.smol-flexbox-grid {
--min: 10ch;
--gap: 1rem;display: flex;
flex-wrap: wrap;
gap: var(--gap);
}
.smol-flexbox-grid > * {
flex: 1 1 var(--min);
}
Smol Modern Centering
Put down the CSS centering jokes! This modern update is often the solution you’re looking for to solve your centering woes.
CSS for “Smol Modern Centering”
.smol-centering {
display: grid;
place-content: center;
}
Smol Responsive Padding
This smol demo is using clamp()
for responsive padding. The order of clamp()
values can be interpreted as: the minimum allowed value is 1rem
, the ideal value is 5%
(which will be relative to the element), and the max allowed value is 3rem
.
In other words, as the element is placed in different contexts and resized across viewports, that value will grow and shrink. But it will always compute to a value within the range of 1rem
to 3rem
.
Another suggested option for the middle ideal value is to use a viewport unit, like 4vw
, which works great for components such as models or setting padding on the body
.
CSS for “Smol Responsive Padding”
.smol-responsive-padding {
padding: 1.5rem clamp(1rem, 5%, 3rem);
}
Gummi bears gummies cheesecake donut liquorice sweet roll lollipop chocolate cake macaroon. Dragée powder biscuit. Dessert topping jelly beans liquorice cake sesame snaps oat cake chocolate bar marshmallow. Cookie danish jelly-o pudding tart chocolate. Jelly sweet tiramisu fruitcake dessert muffin chocolate cake dragée. Donut dragée carrot cake icing. Macaroon lemon drops muffin.
Smol Aspect Ratio Gallery
The aspect-ratio
property is nearing support in all major modern browsers, and by combining it with object-fit
and flexbox, we can create a smol responsive gallery. If you’re using Chrome or Edge, check out the CSS via your your browser Inspector to modify the CSS custom properties on .smol-aspect-ratio-gallery
and see how they affect this layout.
For now, we’re using @supports
checks to fallback to setting a height for list items if aspect-ratio
is not yet supported to achieve a similar effect cross-browser.
Note that aspect-ratio
isn’t just for images, but any element!
This solution also uses the Smol Responsive Flexbox Grid.
CSS for “Smol Aspect Ratio Gallery”
.smol-aspect-ratio-gallery {
--min: 15rem;
--aspect-ratio: 4/3;
--gap: 0;
}.smol-aspect-ratio-gallery li {
aspect-ratio: var(--aspect-ratio);
}
@supports not (aspect-ratio: 1/1) {
.smol-aspect-ratio-gallery li {
height: max(25vh, 15rem);
}
}
.smol-aspect-ratio-gallery img {
display: block;
object-fit: cover;
width: 100%;
height: 100%;
}
Smol Composable Card Component
This component features aspect-ratio
and leans heavily on the pseudo selectors of :not()
, :is()
, :first-child
, and :last-child
. The result is a composable card component that just works with your desired semantic internal content.
Note: Both aspect-ratio
and :is()
are in progress rolling out to the latest versions of modern browsers, so you may need to create fallbacks for your unique audience and consider this solution as a progressive enhancement. Fallback considerations are included as noted with comments.
This solution also uses the Smol Responsive CSS Grid to contain the cards.
CSS for “Smol Composable Card Component”
.smol-card-component {
--img-ratio: 3/2;display: flex;
flex-direction: column;
box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.35);
border-radius: 0.5rem;
}
.smol-card-component > * + * {
margin-top: 1rem;
}
.smol-card-component > img {
aspect-ratio: var(--img-ratio);
object-fit: cover;
width: 100%;
}
@supports not (aspect-ratio: 1/1) {
.smol-card-component > img {
height: max(18vh, 12rem);
}
}
.smol-card-component > img:first-child {
border-radius: 0.5rem 0.5rem 0 0;
}
.smol-card-component > img:last-child {
border-radius: 0 0 0.5rem 0.5rem;
margin-top: auto;
}
.smol-card-component > *:not(img) {
margin-left: 1rem;
margin-right: 1rem;
}
.smol-card-component > :not(img):first-child {
margin-top: 1rem;
}
.smol-card-component > :last-of-type:not(img):not(:is(h2, h3, h4)) {
margin-bottom: 1rem;
}
.smol-card-component > :not(:-webkit-any(h2,h3,h4)) {
font-size: 0.9rem;
}
.smol-card-component > :not(:is(h2,h3,h4)) {
font-size: 0.9rem;
}
.smol-card-component > a {
align-self: start;
}
-
Headline
Chocolate cake macaroon tootsie roll pastry gummies.
Apple pie jujubes cheesecake ice cream gummies sweet roll lollipop.
-
Headline
Chocolate cake macaroon tootsie roll pastry gummies.
-
Headline
Apple pie jujubes cheesecake ice cream gummies sweet roll lollipop.
Chocolate cake macaroon tootsie roll pastry gummies.
Smol Stack Layout
This smol stacked layout is a grid feature that can often replace older techniques that relied on absolute positioning. It works by defining a single grid-template-area
and then assigning all direct children to that grid-area
. The children are then “stacked” and can take advantage of grid positioning, such as the centering technique in the demo.
At first glance you might not notice, but that’s a video background in the demo. And all we had to do was set width: 100%
to ensure it filled the grid area. Then, we make use of place-self
on the h3
to center it. The rest is completely optional design styling!
Bonus features in this demo include defining the h3
size using clamp()
for viewport relative sizing, and also using aspect-ratio
to size the container to help reduce cumulative layout shift.
CSS for “Smol Stack Layout”
.smol-stack-layout {
display: grid;
grid-template-areas: "stack";
aspect-ratio: 16/9;
background-color: #200070;
}.smol-stack-layout > * {
grid-area: stack;
}
.smol-stack-layout video {
width: 100%;
}
.smol-stack-layout h3 {
place-self: center;
font-size: clamp(2.5rem, 5vw, 5rem);
text-align: center;
line-height: 1;
font-style: italic;
padding: 5vh 2vw;
}
.smol-stack-layout small {
align-self: end;
justify-self: start;
padding: 0 0 0.25em 0.5em;
opacity: 0.8;
font-size: 0.8rem;
}
.smol-stack-layout h3,
.smol-stack-layout small {
position: relative;
color: #fff;
}
Into The Unknown
Video from Pexels
Smol Avatar List Component
This smol component, which you may also know as a facepile, is possible due to the ability of CSS grid to easily create overlapping content. Paired with CSS custom properties and calc()
we can make this a contextually resizeable component.
Based on devices capabilities, the grid columns are adjusted to slightly narrower than the --avatar-size
. Since nothing inherent to CSS grid stops the content overflowing, it forces an overlap based on DOM order of the list items. To ensure perfect circle images, we first use the --avatar-size
value to explicitly set the list item dimensions. Then by setting both width and height to 100%
on the img
in addition to object-fit: cover
and border-radius: 50%
, we can be assured that regardless of actual image dimensions the contents will be forced into a circle appearance.
Bonus trick #1 is the use of layered box-shadow
values that only set a spread to create the appearance of borders without adding to the computed dimensions of the image. The spread values are set with em
so that they are relative to the avatar size. And that works because we set the list’s font-size
to --avatar-size
.
Bonus trick #2 is using the general sibling combinator (~
) so that on hover or :focus-within
of an li
, all linked images that follow animate over to reveal more of the hovered avatar. If the number of avatars will cause wrapping, you may want to choose a different effect such as changing the layering via z-index
.
🔎 Pop open your browser devtools and experiment with changing the --avatar-size
value!
CSS for “Smol Avatar List Component”
.smol-avatar-list {
--avatar-size: 3rem;
--avatar-count: 3;display: grid;
grid-template-columns: repeat(
var(--avatar-count),
max(44px, calc(var(--avatar-size) / 1.15))
);
padding: 0.08em;
font-size: var(--avatar-size);
}
@media (any-hover: hover) and (any-pointer: fine) {
.smol-avatar-list {
grid-template-columns: repeat(
calc(var(--avatar-count) + 1),
calc(var(--avatar-size) / 1.75)
);
}
}
.smol-avatar-list li {
width: var(--avatar-size);
height: var(--avatar-size);
}
.smol-avatar-list li:hover ~ li a,
.smol-avatar-list li:focus-within ~ li a {
transform: translateX(33%);
}
.smol-avatar-list img,
.smol-avatar-list a {
display: block;
border-radius: 50%;
}
.smol-avatar-list a {
transition: transform 180ms ease-in-out;
}
.smol-avatar-list img {
width: 100%;
height: 100%;
object-fit: cover;
background-color: #fff;
box-shadow: 0 0 0 0.05em #fff, 0 0 0 0.08em rgba(0, 0, 0, 0.15);
}
.smol-avatar-list a:focus {
outline: 2px solid transparent;
box-shadow: 0 0 0 0.08em #29344B, 0 0 0 0.12em white;
}
Smol Transitions
This set of performant CSS transition utility classes include CSS custom properties for scaling the transition property and duration. We’re doing a few things in this demo that you may want to keep in mind if you use them.
First, we’re triggering the transition of the child elements on :hover
of the parent. The reason for this is that for transitions that move the element, it could end up moving out from under the mouse and causing a flicker between states. The rise
transition is particularly in danger of that behavior.
Second, we wrap our effect class in a media query check for prefers-reduced-motion: reduce
that instantly jumps the transition to the final state. This is to comply with the request for reduced motion by effectively disabling the animated part of the transition.
CSS for “Smol Transitions”
.smol-transitions > * {
--transition-property: transform;
--transition-duration: 180ms;transition: var(--transition-property) var(--transition-duration) ease-in-out;
}
.rise:hover > * {
transform: translateY(-25%);
}
.rotate:hover > * {
transform: rotate(15deg);
}
.zoom:hover > * {
transform: scale(1.1);
}
.fade > * {
--transition-property: opacity;
--transition-duration: 500ms;
}
.fade:hover > * {
opacity: 0;
}
@media (prefers-reduced-motion: reduce) {
.smol-transitions > * {
--transition-duration: 0.01ms;
}
}
Smol Scroll Snap
Modern CSS has gifted us a series of properties that enable setting up more controlled scrolling experiences. In this demo, you’ll find that as you begin to scroll, the middle items “snap” to the center of the scrollable area. Additionally, you are unable to scroll past more than one item at a time.
To align the scroll items, we’re using grid and updating the orientation of child items using grid-auto-flow: column
. Then the width of the grid children is set using min()
which selects the minimum computed value between the options provided. The selected width options in this demo results in a large section of neighboring items being visible in the scrollable area for large viewports, while on smaller viewports the scrollable area is mostly consumed by the current scroll item.
While this is a very cool feature set, use with care! Be sure to test your implementation to ensure its not inaccessible. Test across a variety of devices, and with desktop zoom particularly at levels of 200% and 400% to check for overlap and how a changed aspect ratio affects scroll items. Try it out with a screen reader and make sure you can navigate to all content.
Note: Have caution when attempting to mix fullscreen scroll snap slideshows followed by normal flow content. This can damage the overall scrolling experience and even prevent access to content. Fullscreen scroll areas are also prone to issues for users of high desktop zoom due to high risk of overlapping content as the aspect ratio changes. In addition, fullscreen versions that use y mandatory
result in “scroll hijacking” which can be frustrating to users.
Also – you may have a pleasant smooth scroll experience on a touchpad or magic mouse. But mouse users who rely on interacting with the scroll bar arrows or use a click wheel can have a jarring experience. This is due to browser and OS inconsistencies in handling the snapping based on input method (an issue was specifically reported for this demo using Chrome and Edge on PC).
CSS for “Smol Scroll Snap”
.smol-scroll-snap {
display: grid;
grid-auto-flow: column;
grid-gap: 1.5rem;
overflow-x: auto;
scroll-snap-type: x mandatory;
padding: 0 0 1.5rem;
-webkit-overflow-scrolling: touch;
}.smol-scroll-snap > * {
width: min(45ch, 60vw);
scroll-snap-align: center;
scroll-snap-stop: always;
}