PavelLaptev / css-houdini-squircle

A tiny CSS Houdini module that allows to add a squircle shape to HTML elements
https://pavellaptev.github.io/squircle-houdini-css/
MIT License
356 stars 13 forks source link

Example of using squircle with css variables. #19

Open johannesmutter opened 1 year ago

johannesmutter commented 1 year ago

Hey there!

Here's an example CSS snippet on how to use squircles effectively with CSS variables (to increase performance and avoid repaints when using both a fill and outline).

Firstly, the .squircle class sets some basic styles such as background color, border color, and border width, using CSS variables. You would only need to adjust these variables when used in different contexts.

.squircle {
    --squircle-radius: var(--border-radius-large);
    --squircle-smooth: 0.7;
    --background-color: #EEE;
    --border-color: red;
    --border-hover-color: black;
    --border-width: 2px;
    background-color: var(--background-color);
}

The second part provides a fallback for browsers that don't support CSS Houdini & the paint api (identified by the absence of a .supports-paint-api class on the html element). It uses a standard border and border-radius.

html:not(.supports-paint-api) .squircle {
    border: var(--border-width) solid var(--border-color);
    border-radius: var(--squircle-radius);
}

If the browser supports Houdini (identified by the presence of a .supports-paint-api class on the html element), we set a mask image using the paint(squircle) function and provide padding.

html.supports-paint-api .squircle {
    --squircle-shape: paint(squircle);
    padding: var(--border-width);       
    mask-image: var(--squircle-shape);
    -webkit-mask-image: var(--squircle-shape);
    --squircle-outline: 0;
}

Next, we use a pseudo-element to display an outline for our squircle. We also set a transition effect for smoother visual changes.

html.supports-paint-api .squircle::before {
    content: "";
    display: block;
    position: absolute;
    z-index:0;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: var(--border-color);
    transition: all 0.2s ease;
    mask-image: var(--squircle-shape);
    -webkit-mask-image: var(--squircle-shape);
    --squircle-outline: var(--border-width);
}

We have also provided a style for when our squircle is in focus or when the user hovers over it (.squircle.highlightOnHover:hover::before, .squircle:focus-visible::before), changing the border color to the css variable --border-hover-color.

html.supports-paint-api .squircle.highlightOnHover:hover::before, 
html.supports-paint-api .squircle:focus-visible::before {
    background-color: var(--border-hover-color);
}

Finally, the last block of code applies some specific styles to the first child element of any element with the .squircle class. For example if you have an image that's edge to edge and should be cropped you'll need this, since overflow: hidden doesn't work. The mask is scaled to 150% so it doesn't crop the bottom. Similarly when you would have an image or node with background color at the end, you would create this with > *:last-child

html.supports-paint-api .squircle > *:first-child {
    --squircle-outline: 0;
    --squircle-radius: 14px; 
    /* Unfortunately css calc doesn't work yet. 
    But if you're using SCSS you can replace the static value (in this example: 14px) with a formula:     
    calc ( var(--border-width) - var(--border-width) ) 
    */
    mask-image: var(--squircle-shape);
    mask-size: 100% 150%;
    -webkit-mask-image: var(--squircle-shape);
    -webkit-mask-size: 100% 150%;
}

In a framework like Svelte or React you would import the squircle.min.js like below:

let isWorkletLoaded = false;
export default async function squircle() {
  if (typeof window !== "undefined" && "paintWorklet" in CSS) {
    if (!isWorkletLoaded) {
      await CSS.paintWorklet.addModule('/squircle.min.js');
      isWorkletLoaded = true;
      document.documentElement.classList.add('supports-paint-api');
    }
  }
}

This should provide you a starting point to start using squircles in your project.

johannesmutter commented 1 year ago

Here's everything together for easy copy & paste:


/* Squircle CSS Variables and Basic Styles */
.squircle {
    --squircle-radius: var(--border-radius-large);
    --squircle-smooth: 0.7;
    --background-color: #EEE;
    --border-color: red;
    --border-hover-color: black;
    --border-width: 2px;
    background-color: var(--background-color);
}

/* Squircle Fallback for Non-Houdini Supporting Browsers */
html:not(.supports-paint-api) .squircle {
    border: var(--border-width) solid var(--border-color);
    border-radius: var(--squircle-radius);
}

/* Squircle Styles for Houdini Supporting Browsers */
html.supports-paint-api .squircle {
    --squircle-shape: paint(squircle);
    padding: var(--border-width);       
    mask-image: var(--squircle-shape);
    -webkit-mask-image: var(--squircle-shape);
    --squircle-outline: 0;
}

/* Squircle Outline and Transition Effect */
html.supports-paint-api .squircle::before {
    content: "";
    display: block;
    position: absolute;
    z-index:0;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: var(--border-color);
    transition: all 0.2s ease;
    mask-image: var(--squircle-shape);
    -webkit-mask-image: var(--squircle-shape);
    --squircle-outline: var(--border-width);
}

/* Squircle Hover and Focus Styles */
html.supports-paint-api .squircle.highlightOnHover:hover::before, 
html.supports-paint-api .squircle:focus-visible::before {
    background-color: var(--border-hover-color);
}

/* Squircle Styles for First Child Element */
html.supports-paint-api .squircle > *:first-child {
    --squircle-outline: 0;
    --squircle-radius: 14px; 
    mask-image: var(--squircle-shape);
    mask-size: 100% 150%;
    -webkit-mask-image: var(--squircle-shape);
    -webkit-mask-size: 100% 150%;
}