HTML preprocessors can make writing HTML more powerful or convenient. For instance, Markdown is designed to be easier to write and read for text documents and you could write a loop in Pug.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. So you don't have access to higher-up elements like the <html>
tag. If you want to add classes there that can affect the whole document, this is the place to do it.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. If you need things in the <head>
of the document, put that code here.
The resource you are linking to is using the 'http' protocol, which may not work when the browser is using https.
CSS preprocessors help make authoring CSS easier. All of them offer things like variables and mixins to provide convenient abstractions.
It's a common practice to apply CSS to a page that styles elements such that they are consistent across all browsers. We offer two of the most popular choices: normalize.css and a reset. Or, choose Neither and nothing will be applied.
To get the best cross-browser support, it is a common practice to apply vendor prefixes to CSS properties and values that require them to work. For instance -webkit-
or -moz-
.
We offer two popular choices: Autoprefixer (which processes your CSS server-side) and -prefix-free (which applies prefixes via a script, client-side).
Any URLs 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 its URL and the proper URL extension.
You can apply CSS to your Pen from any stylesheet on the web. Just put a URL to it here and we'll apply it, in the order you have them, before the CSS in the Pen itself.
You can also link to another Pen here (use the .css
URL Extension) and we'll pull the CSS from that Pen and include it. If it's using a matching preprocessor, use the appropriate URL Extension and we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
JavaScript preprocessors can help make authoring JavaScript easier and more convenient.
Babel includes JSX processing.
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.
You can apply a script from anywhere on the web to your Pen. Just put a URL to it here and we'll add it, in the order you have them, before the JavaScript in the Pen itself.
If the script you link to has the file extension of a preprocessor, we'll attempt to process it before applying.
You can also link to another Pen here, and we'll pull the JavaScript from that Pen and include it. If it's using a matching preprocessor, we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
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.
Using packages here is powered by esm.sh, which makes packages from npm not only available on a CDN, but prepares them for native JavaScript ESM usage.
All packages are different, so refer to their docs for how they work.
If you're using React / ReactDOM, make sure to turn on Babel for the JSX processing.
If active, Pens will autosave every 30 seconds after being saved once.
If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.
If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.
Visit your global Editor Settings.
#app
*
box-sizing border-box
background #947cb0
body
align-items center
display flex
justify-content center
font-family 'Arial', sans-serif
min-height 100vh
padding 0
margin 0
overflow hidden
.mask
position fixed
top 50%
left 0
right 0
bottom 0
background #947cb0
.bear
width 100%
background transparent
transform translate(0, 100%)
&__swear
display none
position absolute
left 105%
top 0
background #fff
font-weight bolder
padding 10px
border-radius 8px
&:before
content ''
background #fff
position absolute
top 90%
right 70%
height 30px
width 30px
clip-path polygon(0 100%, 100% 0, 50% 0)
-webkit-clip-path polygon(0 100%, 100% 0, 50% 0)
&__wrap
width 100px
left 50%
position absolute
top 50%
transform translate(-15%, -50%) rotate(5deg) translate(0, -75%)
background transparent
&__arm-wrap
background transparent
position fixed
height 30px
width 90px
z-index 4
top 50%
left 50%
transform translate(0, -50%) rotate(0deg)
&__arm
background transparent
transform-origin left
position absolute
height 100%
width 100%
top 50%
left 50%
transform translate(-35%, -50%) scaleX(1)
&__paw
background #784421
border-radius 100%
position fixed
height 30px
width 30px
z-index 10
top 50%
left 50%
transform-origin right
transform translate(80px, -15px) scaleX(0)
.checkbox
border-radius 50px
height 100px
position fixed
width 200px
z-index 5
top 50%
left 50%
transform translate(-50%, -50%)
[type='checkbox']
cursor pointer
border-radius 50px
position absolute
outline 0
top 0
right 0
bottom 0
left 0
opacity 0
z-index 10
height 100%
width 100%
margin 0
&__bg
background #aaa
border-radius 50px
height 100%
width 100%
z-index 10
&__indicator
background transparent
height 100%
width 50%
border-radius 100%
position absolute
top 0
left 0
&:after
content ''
border-radius 100%
height 85%
width 85%
background #fff
position absolute
top 50%
left 50%
transform translate(-50%, -50%)
const {
React: { useState, useRef, useEffect, Fragment },
ReactDOM: { render },
gsap: {
set,
to,
timeline,
utils: { random },
},
} = window
const rootNode = document.getElementById('app')
const armLimit = random(0, 3)
const headLimit = random(armLimit + 1, armLimit + 3)
const angerLimit = random(headLimit + 1, headLimit + 3)
const armDuration = 0.2
const bearDuration = 0.25
const checkboxDuration = 0.25
const pawDuration = 0.1
const SOUNDS = {
ON: new Audio('https://assets.codepen.io/605876/switch-on.mp3'),
OFF: new Audio('https://assets.codepen.io/605876/switch-off.mp3'),
GROAN: new Audio('https://assets.codepen.io/605876/bear-groan.mp3'),
}
SOUNDS.GROAN.playbackRate = 2
const App = () => {
const [checked, setChecked] = useState(false)
const [count, setCount] = useState(1)
const bearRef = useRef(null)
const swearRef = useRef(null)
const armWrapRef = useRef(null)
const pawRef = useRef(null)
const armRef = useRef(null)
const bgRef = useRef(null)
const indicatorRef = useRef(null)
const onHover = () => {
if (Math.random() > 0.5 && count > armLimit) {
to(bearRef.current, bearDuration / 2, { y: '40%' })
}
}
const offHover = () => {
if (!checked) {
to(bearRef.current, bearDuration / 2, { y: '100%' })
}
}
const onChange = () => {
if (checked) return
setChecked(true)
}
useEffect(() => {
const grabBearTL = () => {
/**
* Different height translations for the bear elements
*
* Paw will go to scaleX 0.8
* Arm scaleX goes down to 0.7
* Arm wrap translates to 50% or 50px
* Bear goes 100% -> 40% -> 0
*/
let bearTranslation
if (count > armLimit && count < headLimit) {
bearTranslation = '40%'
} else if (count >= headLimit) {
bearTranslation = '0%'
}
const onComplete = () => {
setChecked(false)
setCount(count + 1)
}
let onBearComplete = () => {}
if (Math.random() > 0.5 && count > angerLimit)
onBearComplete = () => {
SOUNDS.GROAN.play()
set(swearRef.current, { display: 'block' })
}
const base = armDuration + armDuration + pawDuration
const preDelay = Math.random()
const delay = count > armLimit ? base + bearDuration + preDelay : base
const bearTL = timeline({ delay: Math.random(), onComplete })
bearTL
.add(
count > armLimit
? to(bearRef.current, {
duration: bearDuration,
onComplete: onBearComplete,
y: bearTranslation,
})
: () => {}
)
.to(
armWrapRef.current,
{ x: 50, duration: armDuration },
count > armLimit ? preDelay : 0
)
.to(armRef.current, { scaleX: 0.7, duration: armDuration })
.to(pawRef.current, {
duration: pawDuration,
scaleX: 0.8,
onComplete: () => set(swearRef.current, { display: 'none' }),
})
.to(
bgRef.current,
{
onStart: () => {
SOUNDS.OFF.play()
},
duration: checkboxDuration,
backgroundColor: '#aaa',
},
delay
)
.to(
indicatorRef.current,
{ duration: checkboxDuration, x: '0%' },
delay
)
.to(pawRef.current, { duration: pawDuration, scaleX: 0 }, delay)
.to(
armRef.current,
{ duration: pawDuration, scaleX: 1 },
delay + pawDuration
)
.to(
armWrapRef.current,
{ duration: armDuration, x: 0 },
delay + pawDuration
)
.to(
bearRef.current,
{ duration: bearDuration, y: '100%' },
delay + pawDuration
)
return bearTL
}
const showTimeline = () => {
timeline({
onStart: () => SOUNDS.ON.play(),
})
.to(
bgRef.current,
{ duration: checkboxDuration, backgroundColor: '#2eec71' },
0
)
.to(indicatorRef.current, { duration: checkboxDuration, x: '100%' }, 0)
.add(grabBearTL(), checkboxDuration)
}
if (checked) showTimeline()
}, [checked, count])
return (
<Fragment>
<div className="bear__wrap">
<div ref={swearRef} className="bear__swear">
#@$%*!
</div>
<svg
ref={bearRef}
className="bear"
viewBox="0 0 284.94574 359.73706"
preserveAspectRatio="xMinYMin">
<g id="layer1" transform="translate(-7.5271369,-761.38595)">
<g
id="g5691"
transform="matrix(1.2335313,0,0,1.2335313,-35.029693,-212.83637)">
<path
id="path4372"
d="M 263.90933,1081.4151 A 113.96792,96.862576 0 0 0 149.99132,985.71456 113.96792,96.862576 0 0 0 36.090664,1081.4151 l 227.818666,0 z"
style={{ fill: '#784421', fillOpacity: 1 }}
/>
<path
id="path5634"
d="m 250.42825,903.36218 c 2e-5,66.27108 -44.75411,114.99442 -102.42825,114.99442 -57.674143,0 -98.428271,-48.72334 -98.428251,-114.99442 4e-6,-66.27106 40.754125,-92.99437 98.428251,-92.99437 57.67413,0 102.42825,26.72331 102.42825,92.99437 z"
style={{ fill: '#784421', fillOpacity: 1 }}
/>
<path
id="path5639"
d="m 217,972.86218 c 2e-5,21.53911 -30.44462,42.00002 -68,42.00002 -37.55538,0 -66.000019,-20.46091 -66,-42.00002 0,-21.53911 28.44464,-36 66,-36 37.55536,0 68,14.46089 68,36 z"
style={{ fill: '#e9c6af', illOpacity: 1 }}
/>
<path
id="path5636"
d="m 181.5,944.36218 c 0,8.28427 -20.59974,26.5 -32.75,26.5 -12.15026,0 -34.75,-18.21573 -34.75,-26.5 0,-8.28427 22.59974,-13.5 34.75,-13.5 12.15026,0 32.75,5.21573 32.75,13.5 z"
style={{ fill: '#000000', fillOpacity: 1 }}
/>
<g id="g5681">
<ellipse
style={{ fill: '#784421', fillOpacity: 1 }}
id="path5657"
cx="69"
cy="823.07269"
rx="34.5"
ry="33.289474"
/>
<path
style={{ fill: '#e9c6af', fillOpacity: 1 }}
d="M 69,47.310547 A 24.25,23.399124 0 0 0 44.75,70.710938 24.25,23.399124 0 0 0 64.720703,93.720703 c 0.276316,-0.40734 0.503874,-0.867778 0.787109,-1.267578 1.70087,-2.400855 3.527087,-4.666237 5.470704,-6.798828 1.943616,-2.132591 4.004963,-4.133318 6.179687,-6.003906 2.174725,-1.870589 4.461274,-3.611714 6.855469,-5.226563 2.394195,-1.614848 4.896019,-3.10338 7.498047,-4.46875 0.539935,-0.283322 1.133058,-0.500695 1.68164,-0.773437 A 24.25,23.399124 0 0 0 69,47.310547 Z"
id="ellipse5659"
transform="translate(0,752.36216)"
/>
</g>
<g transform="matrix(-1,0,0,1,300,0)" id="g5685">
<ellipse
ry="33.289474"
rx="34.5"
cy="823.07269"
cx="69"
id="ellipse5687"
style={{ fill: '#784421', illOpacity: 1 }}
/>
<path
transform="translate(0,752.36216)"
id="path5689"
d="M 69,47.310547 A 24.25,23.399124 0 0 0 44.75,70.710938 24.25,23.399124 0 0 0 64.720703,93.720703 c 0.276316,-0.40734 0.503874,-0.867778 0.787109,-1.267578 1.70087,-2.400855 3.527087,-4.666237 5.470704,-6.798828 1.943616,-2.132591 4.004963,-4.133318 6.179687,-6.003906 2.174725,-1.870589 4.461274,-3.611714 6.855469,-5.226563 2.394195,-1.614848 4.896019,-3.10338 7.498047,-4.46875 0.539935,-0.283322 1.133058,-0.500695 1.68164,-0.773437 A 24.25,23.399124 0 0 0 69,47.310547 Z"
style={{ fill: '#e9c6af', fillOpacity: 1 }}
/>
</g>
<ellipse
ry="9.6790915"
rx="9.2701159"
cy="900.38916"
cx="105.83063"
id="path4368"
style={{ fill: '#000000', fillOpacity: 1 }}
/>
<ellipse
style={{ fill: '#000000', fillOpacity: 1 }}
id="ellipse4370"
cx="186.89894"
cy="900.38916"
rx="9.2701159"
ry="9.6790915"
/>
{count >= angerLimit && (
<Fragment>
<path
id="path4396"
d="m 92.05833,865.4614 39.42665,22.76299"
style={{
stroke: '#000000',
strokeWidth: 4.86408424,
strokeLinecap: 'round',
strokeLinejoin: 'round',
strokeMiterlimit: 4,
strokeOpacity: 1,
}}
/>
<path
style={{
stroke: '#000000',
strokeWidth: 4.86408424,
strokeLinecap: 'round',
strokeLinejoin: 'round',
strokeMiterlimit: 4,
strokeOpacity: 1,
}}
d="m 202.82482,865.4614 -39.42664,22.76299"
id="path4400"
/>
</Fragment>
)}
</g>
</g>
</svg>
</div>
<div ref={armWrapRef} className="bear__arm-wrap">
<svg
ref={armRef}
className="bear__arm"
viewBox="0 0 250.00001 99.999997"
preserveAspectRatio="xMinYMin">
<g transform="translate(868.57141,-900.93359)" id="layer1">
<path
style={{ fill: '#784421', fillOpacity: 1 }}
d="m -619.43416,945.05124 c 4.18776,73.01076 -78.25474,53.24342 -150.21568,52.94118 -82.38711,-0.34602 -98.92158,-19.44459 -98.92157,-47.05883 0,-27.61424 4.78794,-42.54902 73.82353,-42.54902 69.03559,0 171.43607,-30.93764 175.31372,36.66667 z"
id="path4971"
/>
<ellipse
style={{ fill: '#e9c6af', fillOpacity: 1 }}
id="path4974"
cx="-683.02264"
cy="950.98572"
rx="29.910826"
ry="29.414362"
/>
</g>
</svg>
</div>
<div ref={pawRef} className="bear__paw" />
<div className="mask" />
<div className="checkbox" onMouseOver={onHover} onMouseOut={offHover}>
<input type="checkbox" onChange={onChange} checked={checked} />
<div ref={bgRef} className="checkbox__bg" />
<div ref={indicatorRef} className="checkbox__indicator" />
</div>
</Fragment>
)
}
render(<App />, rootNode)
Also see: Tab Triggers