Pen Settings

HTML

CSS

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. You can use the CSS from another Pen by using it's URL and the proper URL extention.

+ add another resource

JavaScript

Babel includes JSX processing.

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

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

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.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                mixin confetti()
  - let c = 0
    while c < 10
      .confetti(style=`--rotation: ${(Math.random() * 180) - 90}; --travel: ${Math.random() * -100};`) 🎉
      - c++
mixin cross()
  svg(style!=attributes.style class!=attributes.class viewBox="0 0 100 100")
    path.cross(d="M 80 20 L 20 80" fill="none" stroke-width="10" stroke-linecap="round" stroke-dasharray="100" stroke-dashoffset="100")
    path.cross(d="M 20 20 L 80 80" fill="none" stroke-width="10" stroke-linecap="round" stroke-dasharray="100" stroke-dashoffset="100")
mixin naught()
  svg(class!=attributes.class style!=attributes.style viewBox="0 0 100 100")
    circle(cx="50" cy="50" r="30" fill="none" stroke-width="10" stroke-linecap="round" stroke-dasharray="200" stroke-dashoffset="200")
form.game
  - for (let i = 0; i < 9; i++)
    input(type="checkbox" id=`x-${i}`)
    - const x = i % 3
    - const y = Math.floor(i / 3)
    +cross()(class="x board__x" style=`--x: ${x}; --y: ${y};`)
      path.cross(d="M 80 20 L 20 80" fill="none" stroke-width="10" stroke-linecap="round" stroke-dasharray="100" stroke-dashoffset="100")
      path.cross(d="M 20 20 L 80 80" fill="none" stroke-width="10" stroke-linecap="round" stroke-dasharray="100" stroke-dashoffset="100")
    input(type="checkbox" id=`o-${i}`)
    +naught()(class="o board__o" style=`--x: ${x}; --y: ${y};`)
  .board
    - const DELAYS = [0.75, 0.5, 1, 0.25]
    - for (let l = 0; l < 4; l++)
      - const rotate = l % 2
      - const shift = 2 % (l + 1) ? 1 : -1
      - const delay = DELAYS[l]
      svg.board__line(viewBox="0 0 10 300" style=`--rotate: ${rotate}; --shift: ${shift}; --delay: ${delay};`)
        path(d="M 5 5 L 5 295" stroke-width="10" stroke-linecap="round" stroke-dasharray="300" stroke-dashoffset="300")

    - for (let i = 0; i < 9; i++)
      .board__cell
        label(for=`x-${i}`)
          +cross()(class="x ghost")
        label(for=`o-${i}`)
          +naught()(class="o ghost")
  .game__result.game__result--x.result
    +confetti()
    .result__content
      .result__title Winner!
      +cross()(class="x result__winner")
  .game__result.game__result--o.result
    +confetti()
    .result__content
      .result__title Winner!
      +naught()(class="o result__winner")
  .game__result.game__result--draw.result
    .result__content
      .result__title Draw...
      svg.zzz.result__winner(viewBox="0 0 24 24")
        path(d="M23,12H17V10L20.39,6H17V4H23V6L19.62,10H23V12M15,16H9V14L12.39,10H9V8H15V10L11.62,14H15V16M7,20H1V18L4.39,14H1V12H7V14L3.62,18H7V20Z")
  button(type="reset" title="Reset Board")
    svg.reset(viewBox="0 0 24 24")
      path(d="M17.65,6.35C16.2,4.9 14.21,4 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20C15.73,20 18.84,17.45 19.73,14H17.65C16.83,16.33 14.61,18 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6C13.66,6 15.14,6.69 16.22,7.78L13,11H20V4L17.65,6.35Z")
              
            
!

CSS

              
                @font-face {
  font-family: Cyber;
  src: url("https://assets.codepen.io/605876/Blender-Pro-Bold.otf");
  font-display: swap;
}

*
  box-sizing border-box

:root
  --size 300px
  --piece-size calc(var(--size) / 3)
  --line hsl(0, 0%, 90%)
  --bg hsl(210, 50%, 5%)
  --naught hsl(150, 80%, 70%)
  --naught-alpha hsla(150, 80%, 70%, 0.5)
  --cross hsl(280, 80%, 70%)
  --cross-alpha hsla(280, 80%, 70%, 0.5)
  --draw-speed 0.15
  --color hsl(0, 10%, 15%)

    
    
  @media(min-width 768px)
    --size 50vmin
    
  @media(max-height 500px)
    --size 300px

body
  min-height 100vh
  display grid
  place-items center
  margin 0
  overflow hidden
  background var(--bg)
  color var(--color)
  font-family 'Cyber', sans-serif
  text-transform uppercase

svg
  filter drop-shadow(0 -0.25vmin 0.25vmin hsl(0, 10%, 0%)) drop-shadow(0 0 0.5vmin var(--alpha)) drop-shadow(0 0 1vmin var(--alpha)) drop-shadow(0 0 5vmin var(--stroke)) brightness(1.2)
  stroke var(--stroke)
  
form
.board
  display grid
  place-items center
  position relative

.game__result
  display none
  position absolute
  width calc(var(--size) * 1.5)
  height calc(var(--size) * 1.5)
  transform translate(-50%, -50%)
  top 50%
  left 50%

label
.o
.x
  position absolute
  display inline-block
  height var(--piece-size)
  width var(--piece-size)


label
  cursor pointer

  &:hover .ghost
    opacity 0.5

.ghost
  opacity 0
  transition opacity calc(var(--draw-speed) * 1s)

.o
  --alpha var(--naught-alpha)
  --stroke var(--naught)
  transform rotateX(180deg)

.x
  --alpha var(--cross-alpha)
  --stroke var(--cross)

  path:nth-of-type(2)
    --delay var(--draw-speed)

:checked + .x
  display block

:checked + .o
  display block

.board
  height var(--size)
  width var(--size)
  grid-template-columns repeat(3, 1fr)
  grid-template-rows repeat(3, 1fr)

  &__x
  &__o
    display none
    left calc(var(--x) * (100% / 3))
    top calc(var(--y) * (100% / 3))
    z-index 2
    position absolute

  &__line
    --stroke var(--line)
    --alpha hsla(0, 0%, 90%, 0.5)
    width calc(var(--size) * 0.05)
    height var(--size)
    position absolute
    top 50%
    left 50%
    transform translate(-50%, -50%) rotate(calc(var(--rotate) * -90deg)) translate(calc(var(--shift) * ((var(--size) / 3) * 0.5)), 0)

  &__cell
    height var(--piece-size)
    width var(--piece-size)

input
button
  position absolute

button
  top 125%
  background transparent
  border 0
  padding 0
  height 5vmin
  width 5vmin
  min-height 48px
  min-width 48px
  outline transparent
  cursor pointer
  display none
  transition transform calc(var(--draw-speed) * 1s)
  animation fadeIn calc(var(--draw-speed) * 4s) calc(var(--draw-speed) * 2s) both

  &:hover
    transform translate(0, -4%)
  &:active
    transform translate(0, 2%) scale(0.8)

.reset
  height 100%
  fill var(--line)

input
  position fixed
  left 100%

.result
  animation flyIn calc(var(--draw-speed) * 3s) ease-in both
  backdrop-filter blur(25px)
  z-index 10

  &__content
    height 40%
    width 40%
    top 50%
    left 50%
    transform translate(-50%, -50%)
    position absolute
    border-radius 15%
    background hsla(210, 30%, 20%, 0.8)
    color hsl(0, 0%, 100%)
    align-items center
    display flex
    justify-content center
    flex-direction column
    font-weight bold
    font-size 2rem
    box-shadow 0 3vmin 2.5vmin -2.5vmin hsl(0, 0%, 0%)

  &__winner
    position static
    height calc(var(--size) / 3)

.zzz
  --stroke hsl(210, 80%, 50%)
  fill var(--stroke)

@keyframes fadeIn
  from
    opacity 0

@keyframes flyIn
  from
    opacity 0
    transform translate(-50%, 250%) scale(0)

// This part only cares about showing/hiding the right labels for each move
:checked ~ .board .board__cell label:nth-of-type(odd)
:checked ~ :checked ~ :checked ~ .board .board__cell label:nth-of-type(odd)
:checked ~ :checked ~ :checked ~ :checked ~ :checked ~ .board .board__cell label:nth-of-type(odd)
:checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ .board .board__cell label:nth-of-type(odd)
:checked ~ :checked ~ .board .board__cell label:nth-of-type(even)
:checked ~ :checked ~ :checked ~ :checked ~ .board .board__cell label:nth-of-type(even)
:checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ .board .board__cell label:nth-of-type(even)
:checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ .board .board__cell label:nth-of-type(even)
  // opacity 1
  display block

:checked ~ .board .board__cell label:nth-of-type(even)
:checked ~ :checked ~ :checked ~ .board .board__cell label:nth-of-type(even)
:checked ~ :checked ~ :checked ~ :checked ~ :checked ~ .board .board__cell label:nth-of-type(even)
:checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ .board .board__cell label:nth-of-type(even)
:checked ~ :checked ~ .board .board__cell label:nth-of-type(odd)
:checked ~ :checked ~ :checked ~ :checked ~ .board .board__cell label:nth-of-type(odd)
:checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ .board .board__cell label:nth-of-type(odd)
:checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ .board .board__cell label:nth-of-type(odd)
  // opacity 0.25
  display none

// Winning combos
#x-0:checked ~ #x-1:checked ~ #x-2:checked ~ .game__result--x
#x-3:checked ~ #x-4:checked ~ #x-5:checked ~ .game__result--x
#x-6:checked ~ #x-7:checked ~ #x-8:checked ~ .game__result--x
#x-0:checked ~ #x-3:checked ~ #x-6:checked ~ .game__result--x
#x-1:checked ~ #x-4:checked ~ #x-7:checked ~ .game__result--x
#x-2:checked ~ #x-5:checked ~ #x-8:checked ~ .game__result--x
#x-0:checked ~ #x-4:checked ~ #x-8:checked ~ .game__result--x
#x-2:checked ~ #x-4:checked ~ #x-6:checked ~ .game__result--x
#o-0:checked ~ #o-1:checked ~ #o-2:checked ~ .game__result--o
#o-3:checked ~ #o-4:checked ~ #o-5:checked ~ .game__result--o
#o-6:checked ~ #o-7:checked ~ #o-8:checked ~ .game__result--o
#o-0:checked ~ #o-3:checked ~ #o-6:checked ~ .game__result--o
#o-1:checked ~ #o-4:checked ~ #o-7:checked ~ .game__result--o
#o-2:checked ~ #o-5:checked ~ #o-8:checked ~ .game__result--o
#o-0:checked ~ #o-4:checked ~ #o-8:checked ~ .game__result--o
#o-2:checked ~ #o-4:checked ~ #o-6:checked ~ .game__result--o
  display flex

  // Edge case if the last move is a winning move. Don't show the draw.
  & ~ .game__result--draw
    display none

  & ~ button
    display block

:checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked ~ :checked
  // if the last move is played and there isn't a winner, show a draw
  ~ .game__result--draw
    display block

  ~ button
    display block

.board__line path
.o circle
.x path
  animation draw calc(var(--draw-speed) * 1s) calc(var(--delay, 0) * 1s) ease-in both

@keyframes draw
  to
    stroke-dashoffset 0

.confetti
  position absolute
  top 50%
  left 50%
  font-size 2rem
  animation celebrate 1s forwards, fadeOut calc(var(--draw-speed) * 1s) calc((1 - var(--draw-speed)) * 1s) forwards

@keyframes fadeOut
  to
    opacity 0

@keyframes celebrate
  from
    transform translate(-50%, -50%) rotate(calc(var(--rotation) * 1deg)) scale(0) translate(0, 0)
  to
    transform translate(-50%, -50%) rotate(calc(var(--rotation) * 1deg)) scale(1) translate(0, calc(var(--travel) * 1vmin))
              
            
!

JS

              
                // 404
              
            
!
999px

Console