css Audio - Active file-generic CSS - Active Generic - Active HTML - Active JS - Active SVG - Active Text - Active file-generic Video - Active header Love html icon-new-collection icon-person icon-team numbered-list123 pop-out spinner split-screen star tv

Pen Settings

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

You're using npm packages, so we've auto-selected Babel for you here, which we require to process imports and make it all work. If you need to use a different JavaScript preprocessor, remove the packages in the npm tab.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Use npm Packages

We can make npm packages available for you to use in your JavaScript. We use webpack to prepare them and make them available to import. We'll also process your JavaScript with Babel.

⚠️ This feature can only be used by logged in users.

Code Indentation


Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

HTML Settings

Here you can Sed posuere consectetur est at lobortis. Donec ullamcorper nulla non metus auctor fringilla. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec id elit non mi porta gravida at eget metus. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.

              <!-- markup with 
a button to toggle between night and day colors 
a button with the 3D, hover transition -->
<div class="container">
  <button title="Toggle Theme" class="toggle-theme"></button>
  <button class="hover-button">
    hover on me 😇
              @import url("https://fonts.googleapis.com/css?family=Lato");

define variables for 
- colors of the background
- color of the button element
- color of the button text
- color of the border for the button 
- sizes of the button
:root {
  --background-gradient-top: #000128;
  --background-gradient-bottom: #030a33;
  --button-color: #7548d6;
  --text-color: #eee;
  --button-border-color: #000128;
  --button-height: 64px;
  --button-width: 300px;
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
body {
  min-height: 100vh;
  width: 100%;
  font-family: 'Lato', sans-serif;
  background: linear-gradient(
    to bottom, 
/* center content in the middle of the page */
.container {
  height: 100vh;
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  /* include perspective property on the parent container, giving a subtle perspective on the nested children */
  perspective: 1000px;
/* create a simple round button in the top right corner of the page  */
.container .toggle-theme {
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  background-color: var(--text-color);
  position: absolute;
  top: 1rem;
  right: 1rem;
  cursor: pointer;
  border: none;
  outline: none;
  transition: background-color 1s ease-out;

for the 3D button the hover transition is achieved with a pseudo selector and transform properties  

the line of through is as follows:
- absolute position a pseudo element at the bottom of the button, with the same styling properties (background, border)
- rotate the pseudo element from a transform-origin position of bottom center by 90degrees, hiding said pseudo element perpendicular to the visitor eyesight
- on hover, alter transform properties of the element AND of the pseudo element as to rotate the first out of view in an upward fashion and the second into view in a forward animation

there are a couple of mishaps corrected here and there (for instance, rotating the pseudo-element forces the text inside to be rotated as well), but those are (hopefully) fixed with the commented properties

.container .hover-button {
  /* explicit width and height make life much easier, as the rotation is accompanied by a vertical translation matching (half of) the height of the element */
  height: var(--button-height);
  width: var(--button-width);

  /* position relative to absolute position the pseudo element */
  position: relative;
  /* transition to smoothen the change in properties */
  transition: all 0.5s ease-out;
  /* transform style to allow for the rotation to properly occur */
  transform-style: preserve-3d;

  /* styling properties */
  outline: none;
  background-color: var(--button-color);
  border: 3px solid var(--button-border-color);
  border-radius: 0.5rem;
  font-size: 1.2rem;
  font-weight: bold;
  text-transform: uppercase;
  color: var(--text-color);
  cursor: pointer;  

on hover rotate the element 90degrees out of sight, and move it upwards by a measure of half the height of the container 
this to give the impression of a roll-out effect
.container .hover-button:hover {
  transform: translateY(calc(var(--button-height)/-2)) rotateX(90deg);
.container .hover-button:before {
  content: 'Thank you 😇';
  /* position the pseudo element to stretch the entirety of the connected element */
  position: absolute;
  /* include a minor "padding" as to compensate for the border of 3px in the connected element */
  bottom: -3px;
  left: -3px;
  right: -3px;
  top: -5px;
  /* transform the pseudo element relative to an origin point located in the bottom center of the element  */
  transform-origin: bottom center;
  /* rotate the element out of view, 
  changing also the rotation of the other axes to make the nested content legible (and not mirrored),
  changing also the vertical coordinate as to move the pseudo element below the element by an amount matching the height of the element itself */
  transform: rotateX(90deg) rotateY(180deg) rotateZ(180deg) translateY(var(--button-height));
  /* set the element to be initially hidden */
  opacity: 0;
  /* include a transition to smoothen the change in properties */
  transition: all 0.5s ease-out;

  /* styling properties */
  padding-top: 30 px;
  background-color: var(--button-color);
  border-radius: 0.5rem;
  border: 3px solid var(--button-border-color);
  padding-top: calc(var(--button-height)/4);

/* on hover maintain the properties of rotate and translate
the change happens because the pseudo-element is basically attached to the element which is rotated/ otherwise transformed 
think of this pseudo element as a rectangle making up the bottom of an imaginary box, of which the element shows the front 
rotate the box, and the bottom comes into view  */
.container .hover-button:hover:before {
  transform: rotateX(90deg) rotateY(180deg) rotateZ(180deg) translateY(64px);
  opacity: 1;

              // target the button enabling the change in colors
const toggleThemeButton = document.querySelector(".toggle-theme");

// listen to a click event, at which point call a function to alter the selected CSS variables
toggleThemeButton.addEventListener("click", toggleTheme);

function toggleTheme() {
    // retrieve properties in the root element
    const root = document.querySelector(":root");
    const rootStyles = getComputedStyle(root);
    // based on the value of a variable, toggle between one set of variables and another
    const buttonColor = rootStyles.getPropertyValue("--button-color");
    if(buttonColor == "#eee") {
        root.style.setProperty("--button-color", "#7548d6");
        root.style.setProperty("--background-gradient-top", "#000128");
        root.style.setProperty("--background-gradient-bottom", "#030a33");
        root.style.setProperty("--text-color", "#eee");
    else {
        root.style.setProperty("--button-color", "#eee");
        root.style.setProperty("--background-gradient-top", "#eee");
        root.style.setProperty("--background-gradient-bottom", "#eee");
        root.style.setProperty("--text-color", "#000128");
🕑 One or more of the npm packages you are using needs to be built. You're the first person to ever need it! We're building it right now and your preview will start updating again when it's ready.
Loading ..................