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.

<div class="box" aria-labelledby="box-title event-title">
	<div class="box-header-background"></div>
	<div class="box-header">
		<img src="https://placekitten.com/100/100" alt="" role="presentation" class="box-img" />
		<div class="box-title" id="box-title">Fluffy Meowington</div> <!-- if you make this name longer it should wrap -->
	<div class="box-content">
		<div class="event-title">Event Title</div>
		<p class="event-text">A thing happened in Fluffy's life that is culturally or socially significant!</p>
		<ul class="list list-inline">
<!-- 			Since this area auto-updates based on other interactions, I'd want to think more about setting this as an aria-live region. They're typically irritating to me personally though, so I tend to avoid them. that's why I'd want to think about this some more before just doing it. -->
			<li class="list-item" id="js-interaction-congrats"><span class="count" id="js-count-congrats"></span> congrats</li>
			<li class="list-item" id="js-interaction-likes"><span class="count" id="js-count-likes"></span> like(s)</li>
		<div class="button-bar" role="group">
			<button class="button button-default" id="js-toggle-congrats" aria-pressed="false">Say Congrats!</button>
			<button class="button button-default" id="js-toggle-likes" aria-label="like this event" aria-pressed="false">
				<i class="fa fa-icon fa-thumbs-up fa-2x" aria-hidden="true"></i>
              $height-offset: 90px;
$base-font-size: 16px;
$box-width: 325px;
$background-color: transparent;
$black: #212121; //never use pure black
$gray-dark: #444444;
$interaction: blue;

*, *:after {
	box-sizing: border-box;

body {
	color: $gray-dark;
	font-family: 'Roboto', sans-serif;
	font-size: $base-font-size;
	line-height: 1.5;
main {
	@media screen and (min-width: 1200px){
	padding-left: 1em; //just to make this thing not hug the left wall
.box {
	display: flex;
	flex-flow: row wrap;
	font-size: 16px;
	justify-content: center;
	max-width: 100%;
	min-width: $box-width;
	width: 100%;
	@media screen and (min-width: 500px) { //probably should be ems
		font-size: calc( 16px + 8 * ((100vw - 500px) / 1200));
		width: $box-width; //probably should be ems
		border: 1px solid rgba(0,0,0,0.1);
		border-radius: 4px;
	@media screen and (min-width: 1200px) { //probably should be ems
		font-size: calc( 14px + 8 * ((100vw - 1200px) / 1500)); //look up the right way to do this
	.box-header-background {
		background-image: url(https://placekitten.com/100/100);
		background-size: cover;
		clip-path: inset(0 0 0 0); //cleans up the fuzzy edges (but this doesn't have IE support)
		filter: blur(8px); 
		height: 152px;
		position: absolute; //ew ew ew ew 
		width: $box-width;
		z-index: -1;
	.box-header {
		align-items: flex-end;
		display: flex;
		justify-content: space-between;
		width: 100%;

		.box-img {
			margin-left: 1em;
			transform: translateY(50px); 
			box-shadow: 1px 1px 1px rgba(0,0,0,0.5) //meh about this. Could take it or leave it.
		.box-title { 
			color: white;
			filter: initial;
			flex: 1;
			font-size: 1.125em;
			font-weight: 400;
			letter-spacing: 1px;
			margin-top: $height-offset;
			padding: 0.75em 0.5em 0;
			text-align: right;
			text-shadow: 1px 1px 1px rgba(0,0,0,0.5);
	.box-content {
		background-color: $background-color;
		width: 100%;
		.event-title {
			font-size: 1.125em;
			font-weight: bold;
			margin-top: $height-offset/2;
			padding: 0.75em 0.75em 0;
		.event-text {
			padding: 0.75em;
			margin: 0;

.list {
	display: flex;
	list-style: none;
	margin: 0;
	padding: 0;
	&.list-inline {
		padding: 0.75em;
		.list-item {
			font-size: 0.8em;
			&:first-of-type {
				&:after {
					content: ',';
					padding-right: 1px; //ugh hack
			&.has-interaction {
				color: $interaction;

.button-bar {
	display: flex;
	margin-top: 1em;
	width: 100%;
	.button {
		background-color: lightgray;
		border-bottom-left-radius: 4px;		
		border-top-left-radius: 4px;
		border: 1px solid darkgray;
		font-family: 'Roboto', sans-serif;
		font-size: 0.9em;
		line-height: 1.5;
		padding: 0.75em;
		width: 75%;
		&:nth-of-type(2) {
			border-top-left-radius: 0;
			border-top-right-radius: 4px;
			border-bottom-left-radius: 0;
			border-bottom-right-radius: 4px;
			border-left: none;
			width: 25%; //this also doesn't really make me happy. 
		&:hover {
			cursor: pointer;
.text-success {
	color: green;

//this widget has made me do most of the stuff I dislike in CSS. Everything feels like a hack. 
              //let's try this w/o jQuery.tbh IRL this would be a contextual component in Ember and that would make it a lot easier. 

// declare all your variables first to be polite. 
var congratsCount, likesCount, congratsCountText, likesCountText, incrementCongratsCount, decrementCongratsCount, incrementLikesCount, decrementLikesCount, congratsInteraction, likesInteraction, congratsToggle, likesToggle;

//set the counts
congratsCount = 2;
likesCount = 3;

//"counts" text elements
congratsCountText = document.getElementById('js-count-congrats');
likesCountText = document.getElementById('js-count-likes');

//fill in the count data
congratsCountText.textContent = congratsCount;
likesCountText.textContent = likesCount;

//Increment/Decrement interactions
incrementCongratsCount = congratsCount + 1;
decrementCongratsCount = congratsCount - 1;
incrementLikesCount = likesCount + 1;
decrementLikesCount = likesCount -1;

//Interactions elements (we want the whole word, not just the count number)
congratsInteraction = document.getElementById('js-interaction-congrats')
likesInteraction = document.getElementById('js-interaction-likes');

//Toggle Buttons
congratsToggle = document.getElementById('js-toggle-congrats');
likesToggle = document.getElementById('js-toggle-likes');

//congrats toggle button
congratsToggle.addEventListener('click', function(){
	if (congratsInteraction.classList.contains('has-interaction')) {
			congratsCount--;  //decrement the count by one
			congratsCountText.textContent = congratsCount; //update the element with the new value
			congratsInteraction.classList.remove('has-interaction'); //remove the interaction indicator
		  congratsToggle.setAttribute('aria-pressed', 'false'); //update the aria-pressed property
			} else {
				congratsCount++; //increment the count by one
				congratsCountText.textContent = congratsCount; //update the element with the new value
				congratsInteraction.classList.add('has-interaction'); //add the interaction indicator
				congratsToggle.setAttribute('aria-pressed', 'true'); //update the aria-pressed property

//likes toggle button
likesToggle.addEventListener('click', function() {
	if (likesInteraction.classList.contains('has-interaction')) {
		likesCount--; //decrement the count by one
		likesCountText.textContent = likesCount; //update the element with the new value
		likesInteraction.classList.remove('has-interaction'); //remove the interaction indicator
		likesToggle.setAttribute('aria-pressed', 'false'); //update the aria-pressed property
		likesToggle.classList.remove('text-success'); //remove visual indicator
	} else {
		likesCount++; //increment count by one
		likesCountText.textContent = likesCount; //update the element with the new value
		likesInteraction.classList.add('has-interaction'); //add the interaction indicator
		likesToggle.setAttribute('aria-pressed', 'true'); //update the aria-pressed property
		likesToggle.classList.add('text-success'); //give a visual indicator 

// I suppose the "say congrats" toggle should not really be a toggle but rather send a message of congrats to Fluffy (in addition to the things it already does) but then what if you toggle the button again? Will the congrats message go away? Fluffy would be sad. this interaction doesn't feel "on purpose" enough, I need to think about this some more. 

// maybe the "say congrats" button could have a visual indicator that the user had left a congrats message (add a checkmark or something?)

//Also is this really how tedious plain JS is? It's so tedious. I want Ember back please. 
🕑 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 ..................