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.
<svg width="0" height="0" display="none">
<symbol id="flagBlack" viewBox="0 0 24 24">
<path id="flag" d="M 6,4 V 20 A 1,1 0 0 0 8,20 V 13.6992 L 18.3418,9.93945 A 1,1 0 0 0 18.3418,8.06055 L 8,4.30078 V 4 A 1,1 0 0 0 6,4 Z" />
</symbol>
<symbol id="flagBlue" viewBox="0 0 24 24">
<use xlink:href="#flag" />
</symbol>
<symbol id="bombBlack" viewBox="48 0 24 24">
<path id="bomb" d="M 59,6.08594 A 6,6 0 0 0 55.377,8.17773 L 53.5723,7.13477 A 1,1 0 0 0 52.5723,8.86523 L 54.3828,9.91016 A 6,6 0 0 0 54.3809,14.0898 L 52.5723,15.1348 A 1,1 0 0 0 53.5723,16.8652 L 55.3809,15.8203 A 6,6 0 0 0 59,17.9102 V 20 A 1,1 0 0 0 61,20 V 17.9141 A 6,6 0 0 0 64.623,15.8223 L 66.4277,16.8652 A 1,1 0 0 0 67.4277,15.1348 L 65.6172,14.0898 A 6,6 0 0 0 65.6191,9.91016 L 67.4277,8.86523 A 1,1 0 0 0 66.4277,7.13477 L 64.6191,8.17969 A 6,6 0 0 0 61,6.08984 V 4 A 1,1 0 0 0 59,4 Z" />
</symbol>
<symbol id="bombRed" viewBox="48 0 24 24">
<use xlink:href="#bomb" />
</symbol>
<symbol id="one" viewBox="0 24 24 24">
<path d="M 13.1043,28.964 8.4403,30.174 C 7.5603,30.394 7.2743,30.658 7.2743,31.296 7.2743,31.934 7.7143,32.44 8.2863,32.44 8.5063,32.44 8.5723,32.418 8.9903,32.308 L 10.9043,31.802 V 40.8 H 8.6823 C 8.1103,40.8 7.9563,40.822 7.7583,40.954 7.4283,41.152 7.2303,41.526 7.2303,41.9 7.2303,42.23 7.4063,42.582 7.6923,42.78 7.8683,42.934 8.1763,43 8.6823,43 H 15.3263 C 15.7663,43 16.0963,42.934 16.2503,42.846 16.5803,42.626 16.7783,42.274 16.7783,41.9 16.7783,41.548 16.6023,41.218 16.3163,40.998 16.0963,40.844 15.9203,40.8 15.3263,40.8 H 13.1043 Z" />
</symbol>
<symbol id="two" viewBox="24 24 24 24">
<path d="M 33.9583,40.8 C 35.8943,39.128 37.1703,38.028 37.7643,37.478 39.9423,35.498 40.4703,34.662 40.4703,33.232 40.4703,30.79 38.4683,28.964 35.7843,28.964 34.4863,28.964 33.2323,29.404 32.3523,30.196 31.6043,30.878 31.0323,31.956 31.0323,32.66 31.0323,33.232 31.5383,33.716 32.1323,33.716 32.5943,33.716 33.0123,33.452 33.1223,33.078 33.3643,32.374 33.3863,32.33 33.6063,32.066 34.0903,31.494 34.8603,31.164 35.7403,31.164 37.1703,31.164 38.2703,32.022 38.2703,33.144 38.2703,33.892 37.8963,34.376 35.6963,36.378 34.7063,37.28 34.2883,37.632 30.5923,40.69 V 43 H 40.5803 V 41.064 C 40.5803,40.536 40.5363,40.338 40.4263,40.14 40.2283,39.81 39.8543,39.612 39.4803,39.612 38.7983,39.612 38.4463,40.008 38.3803,40.8 Z" />
</symbol>
<symbol id="three" viewBox="48 24 24 24">
<path d="M 59.4763,34.574 C 58.8163,34.574 58.3323,35.036 58.3323,35.674 58.3323,36.268 58.7503,36.642 59.4763,36.686 60.4223,36.73 60.7083,36.774 61.1703,36.972 62.2043,37.39 62.8423,38.16 62.8423,38.974 62.8423,39.48 62.6003,40.03 62.2263,40.382 61.6323,40.91 60.8183,41.13 59.3003,41.13 57.8923,41.13 57.2323,40.998 56.6603,40.624 56.3083,40.404 56.2203,40.36 55.9563,40.36 55.3403,40.36 54.8563,40.844 54.8563,41.46 54.8563,42.582 56.6603,43.33 59.3663,43.33 61.2803,43.33 62.4463,43.022 63.4143,42.296 64.4263,41.504 65.0423,40.272 65.0423,38.996 65.0423,37.588 64.2283,36.488 62.5563,35.674 63.9863,34.816 64.5583,34.002 64.5583,32.792 64.5583,31.89 64.1623,30.922 63.4583,30.24 62.6223,29.382 61.4783,28.964 60.0263,28.964 57.6723,28.964 55.4943,30.13 55.4943,31.406 55.4943,32 55.9783,32.506 56.5723,32.506 56.8803,32.506 57.1663,32.374 57.3643,32.176 58.0683,31.406 58.6843,31.164 59.8943,31.164 61.4123,31.164 62.3583,31.802 62.3583,32.836 62.3583,33.76 61.4343,34.574 60.3563,34.574 Z" />
</symbol>
<symbol id="four" viewBox="72 24 24 24">
<path d="M 87.2383,29.316 H 84.3563 L 79.0543,38.138 V 39.964 H 85.0383 V 40.8 H 84.2023 C 83.6743,40.8 83.4763,40.822 83.2783,40.954 82.9483,41.152 82.7503,41.526 82.7503,41.9 82.7503,42.23 82.9263,42.582 83.2123,42.78 83.3883,42.934 83.6963,43 84.2023,43 H 87.1063 C 87.5683,43 87.8983,42.934 88.0523,42.846 88.3823,42.626 88.5803,42.274 88.5803,41.9 88.5803,41.196 88.0963,40.8 87.2383,40.8 V 39.964 C 88.1183,39.942 88.5803,39.568 88.5803,38.864 88.5803,38.16 88.0963,37.764 87.2383,37.764 V 29.316 M 85.0383,37.764 H 81.7383 L 85.0383,32.44 Z" />
</symbol>
<symbol id="five" viewBox="0 48 24 24">
<path d="M 10.2223,55.516 H 14.6663 C 15.1283,55.516 15.4363,55.472 15.5903,55.362 15.9203,55.164 16.1183,54.79 16.1183,54.416 16.1183,54.064 15.9423,53.734 15.6783,53.514 15.4363,53.36 15.2603,53.316 14.6663,53.316 H 8.0223 V 59.564 C 8.0223,60.268 8.4843,60.796 9.1223,60.796 9.3203,60.796 9.4523,60.752 9.7383,60.62 10.6843,60.136 11.6523,59.872 12.3783,59.872 13.8523,59.872 14.8423,60.95 14.8423,62.578 14.8423,64.316 13.7863,65.13 11.4983,65.13 10.0683,65.13 9.4083,64.954 8.7483,64.404 8.4183,64.14 8.2643,64.074 7.9563,64.074 7.3623,64.074 6.8563,64.558 6.8563,65.152 6.8563,66.384 8.8803,67.33 11.5203,67.33 13.5883,67.33 14.8643,66.89 15.8323,65.834 16.6243,64.976 17.0423,63.832 17.0423,62.556 17.0423,59.74 15.0623,57.672 12.4003,57.672 11.7623,57.672 11.0143,57.804 10.2223,58.046 Z" />
</symbol>
<symbol id="six" viewBox="24 48 24 24">
<path d="M 33.9803,59.872 C 34.4863,57.166 36.5103,55.23 38.8423,55.23 39.0403,55.23 39.1503,55.252 39.3923,55.34 39.7443,55.472 39.9643,55.516 40.1623,55.516 40.7783,55.516 41.2843,55.01 41.2843,54.394 41.2843,53.536 40.2503,52.964 38.7103,52.964 36.6863,52.964 34.8823,53.888 33.4083,55.692 32.2863,57.1 31.7143,58.772 31.7143,60.73 31.7143,62.314 32.1103,64.03 32.6823,65.086 33.5183,66.582 34.8603,67.33 36.6863,67.33 38.1603,67.33 39.2383,66.912 40.0303,66.032 40.7783,65.218 41.1743,64.096 41.1743,62.842 41.1743,60.356 39.3043,58.332 36.9943,58.332 35.8723,58.332 35.0363,58.75 33.9803,59.872 M 34.2003,62.402 C 34.7943,61.324 35.8943,60.532 36.8403,60.532 38.0283,60.532 38.9743,61.544 38.9743,62.82 38.9743,64.25 38.1383,65.13 36.7523,65.13 35.3223,65.13 34.5303,64.316 34.2003,62.402 Z" />
</symbol>
<symbol id="seven" viewBox="48 48 24 24">
<path d="M 62.2703,55.516 58.9703,65.218 C 58.8603,65.592 58.8163,65.768 58.8163,65.966 58.8163,66.538 59.3443,67.022 59.9383,67.022 60.5103,67.022 60.7963,66.736 61.0823,65.944 L 64.5803,55.494 V 53.316 H 55.0543 V 55.252 C 55.0543,55.78 55.0983,55.978 55.2083,56.176 55.4063,56.506 55.7803,56.704 56.1543,56.704 56.8363,56.704 57.1883,56.308 57.2543,55.516 Z" />
</symbol>
<symbolid="eight" viewBox="72 48 24 24">
<path d="M 86.6443,59.982 C 87.9643,59.102 88.5803,58.156 88.5803,56.946 88.5803,54.746 86.5343,52.964 84.0043,52.964 81.4523,52.964 79.4283,54.746 79.4283,56.99 79.4283,58.156 80.0443,59.102 81.3643,59.982 79.9563,60.862 79.2303,62.006 79.2303,63.37 79.2303,65.724 81.1663,67.33 83.9823,67.33 86.8423,67.33 88.7783,65.724 88.7783,63.37 88.7783,62.006 88.0523,60.862 86.6443,59.982 M 84.0043,55.164 C 85.4123,55.164 86.3803,55.956 86.3803,57.1 86.3803,58.2 85.3903,58.992 84.0043,58.992 82.6183,58.992 81.6283,58.2 81.6283,57.078 81.6283,55.956 82.5963,55.164 84.0043,55.164 M 84.0043,61.214 C 85.4563,61.214 86.5783,62.094 86.5783,63.216 86.5783,64.316 85.5003,65.13 84.0043,65.13 82.5083,65.13 81.4303,64.316 81.4303,63.194 81.4303,62.094 82.5963,61.214 84.0043,61.214 Z" />
</symbol>
</svg>
<div id="gamepad"></div>
#flagBlack, #bombBlack, #eight {
fill: #000;
}
#flagBlue, #one {
fill: #0000ff;
}
#bombRed {
fill: #cc0000;
}
#two {
fill: #00aa44;
}
#three {
fill: #d45500;
}
#four {
fill: #aa0000;
}
#five {
fill: #808000;
}
#six {
fill: #0088aa;
}
#seven {
fill: #aa0088;
}
body {
font-family: sans-serif;
font-size: 13px;
}
#gamepad {
display: flex;
flex-direction: column;
width: 900px;
align-items: center;
}
.selector {
display: flex;
justify-content: center;
margin: 0 0 2em;
padding: 0;
list-style-type: none;
color: black;
font-weight: 700;
}
.selector li {
display: block;
padding: 0.6em 1em;
margin: 0 0.5em;
border: 1px solid #888;
border-radius: 4px;
cursor: pointer;
}
.selector li:hover {
background: #00208840;
}
.selector li:active {
background: #202088dd;
color: white;
}
.result {
margin: 0 0 2em;
}
.result label {
display: inline-block;
width: 6em;
text-align: right;
}
.result span {
display: inline-block;
padding: 0.3em;
margin: 0 0 0 1em;
text-align: center;
width: 4em;
height: 1.4em;
line-height: 1.4em;
border: 1px solid #888;
border-radius: 4px;
}
.result.won span {
background-color: #88cc88;
}
.result.lost span {
background-color: #dd7777;
}
.bg {
fill: #eee;
pointer-events: none;
}
.under {
opacity: 0;
}
.field.hidden {
fill: #ccc;
}
svg:not(.ended) .under.active:hover + .field.hidden {
fill: #ddd;
}
.field.shown {
fill: #fff;
}
.ended .field.hidden {
fill: #888;
}
.ended .field.wrong {
fill: #dd7777;
}
.mark, .field {
pointer-events: none;
}
const base = {
WAITING: 0,
RUNING: 1,
WON: 2,
LOST: 3,
ends: [null, null, "won", "lost"],
UNMARKED: 0,
BLACKMARK: 1,
BLUEMARK: 2,
marks: [null, "flagBlack", "flagBlue"],
HIDDEN: 0,
SOLVED: 1,
EXPLODED: 2,
buttons: ["hidden", "shown", "wrong"],
bombs: ["bombBlack", "bombRed"],
numbers: [
null,
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight"
],
fieldsize: 30,
levels: {
'small': {
desc: 'Small (8×8)',
width: 8,
height: 8,
quota: 10
},
'mid': {
desc: 'Mid (16×16)',
width: 16,
height: 16,
quota: 40
},
'large': {
desc: 'Large (30×16)',
width: 30,
height: 16,
quota: 99
}
}
}
class Game extends React.Component {
constructor (props) {
super(props);
this.state = {
...base.levels.small,
found: 0,
mid: false,
run: base.WAITING
};
}
selectSize (level) {
this.setState({
...level,
found: 0,
run: base.WAITING
});
}
onMarked (count) {
this.setState({found: this.state.found + count});
}
onRun (run) {
this.setState({run: run});
}
render () {
return <React.Fragment>
<Selector
selectSize = { this.selectSize.bind(this) }
/>
<p
className={ "result " + base.ends[this.state.run] || "" }
><label>Marked:</label><span>{
this.state.found} / { this.state.quota
}</span><Clock run = { this.state.run } />
</p>
<Area
onRun = { this.onRun.bind(this) }
onMarked = { this.onMarked.bind(this) }
{ ...this.state }
/>
</React.Fragment>
}
}
class Clock extends React.Component {
constructor (props) {
super(props);
this.state = {
dur: 0
};
}
start () {
const time = Date.now();
this.timerID = setInterval(() => {
const elapsed = Math.floor((Date.now() - time) / 1000);
this.setState({dur: elapsed});
}, 100);
}
stop () {
clearInterval(this.timerID);
}
componentDidUpdate(prevProps) {
if (this.props.run === prevProps.run) return;
switch (this.props.run) {
case base.WAITING:
this.stop();
return this.setState({dur: 0});
case base.RUNING:
return this.start();
case base.WON:
case base.LOST:
return this.stop();
}
}
componentWillUnmount () {
this.stop();
}
formatDuration (dur) {
return Math.floor(dur / 60) + ':' +
((dur % 60) / 100).toFixed(2).split('.')[1]
}
render () {
return <React.Fragment>
<label>Timer:</label><span>{ this.formatDuration(this.state.dur) }</span>
</React.Fragment>;
}
}
class Selector extends React.Component {
render () {
return <ul className = "selector">
{ Object.entries(base.levels).map(([key, level]) => <li
key = { key }
onClick = { () => this.props.selectSize(level) }
>{ level.desc }</li>) }
</ul>
}
}
function seedFields (props) {
const {width, height, quota} = props;
const total = width * height;
// seed field data array
const fields = new Array(total).fill(0).map((v, i) => {
const col = i % width,
line = Math.floor(i / width);
return {
col, line,
neighbours: getNeighbours(col, line, props),
full: false,
marked: base.UNMARKED,
revealed: base.HIDDEN,
highlight: false
}
});
// randomly position bombs
let bombs = quota;
while (bombs) {
const place = Math.floor(Math.random() * total);
if (fields[place].full) continue;
fields[place].full = true;
bombs--;
}
// provide hints
for (const field of fields) {
field.hint = field.neighbours.reduce((sum, idx) => {
return sum + fields[idx].full;
}, 0);
}
return fields;
}
function getNeighbours (x, y, props) {
const n = [];
for (let i of [-1, 0, 1]) {
const nx = x + i;
for (let j of [-1, 0, 1]) {
const ny = y + j;
if (nx >= 0 && nx < props.width &&
ny >= 0 && ny < props.height) {
n.push(nx + ny * props.width);
}
}
}
return n;
}
class Area extends React.Component {
constructor (props) {
super(props);
this.state = {
fields: seedFields(props)
};
}
static getDerivedStateFromProps(props) {
if (props.run === base.WAITING) {
return {
fields: seedFields(props)
};
}
return null;
}
loopNeighbours (field, callback) {
for (const idx of field.neighbours) {
callback.bind(this)(this.state.fields[idx]);
};
}
highlight (field) {
field.highlight = true;
}
reveal (field) {
if (field.revealed || field.marked) return;
if (field.full) {
field.revealed = base.EXPLODED;
this.props.onRun(base.LOST);
} else {
field.revealed = base.SOLVED;
if (!field.hint) {
this.loopNeighbours(field, this.reveal);
}
}
}
cautiousHighlight (field) {
if (!field.revealed) return;
this.loopNeighbours(field, this.highlight);
}
cautiousReveal (field) {
if (!field.revealed) return;
const marks = field.neighbours.filter(idx => this.state.fields[idx].marked).length;
if (marks === field.hint) {
this.loopNeighbours(field, this.reveal);
}
}
mark (field) {
const choice = this.props.mid ? 3 : 2;
if (field.marked === base.UNMARKED) {
this.props.onMarked(1);
} else if (field.marked === base.BLACKMARK) {
this.props.onMarked(-1);
}
field.marked = (field.marked + 1) % choice;
}
getTargetField (event) {
const col = event.target.getAttribute('x') / base.fieldsize;
const line = event.target.getAttribute('y') / base.fieldsize;
return this.state.fields[line * this.props.width + col];
}
catch (e) {
e.preventDefault();
e.stopPropagation();
}
mousedown (event) {
this.catch(event);
if (this.props.run === base.WAITING) this.props.onRun(base.RUNING);
if (this.props.run > base.RUNING) return;
const field = this.getTargetField(event);
switch (event.button) {
case 0:
this.highlight(field);
break;
case 1:
this.cautiousHighlight(field);
break;
case 2:
this.mark(field);
break;
}
this.setState({fields: this.state.fields});
}
mouseup (event) {
this.catch(event);
if (this.props.run > base.RUNING) return;
const field = this.getTargetField(event);
switch (event.button) {
case 0:
this.reveal(field);
break;
case 1:
this.cautiousReveal(field);
break;
}
if (field.revealed === base.EXPLODED) return;
const hidden = this.state.fields.filter(field => !field.revealed).length;
if (hidden == this.props.quota) {
this.props.onRun(base.WON);
}
this.state.fields.forEach(field => field.highlight = false);
this.setState({fields: this.state.fields});
}
renderField (field) {
return <Field
{ ...field}
run = { this.props.run }
key = { `frg-${field.col}-${field.line}` }
/>;
}
render () {
const w = this.props.width * base.fieldsize,
h = this.props.height * base.fieldsize;
return <svg
width = { w }
height = { h }
viewBox = { `0 0 ${w} ${h}` }
className = { this.props.run > base.RUNING ? "ended" : null }
onMouseDown = { this.mousedown.bind(this) }
onMouseUp = { this.mouseup.bind(this) }
onContextMenu = { this.catch.bind(this) }
>
<rect
width = "100%"
height = "100%"
className = "bg"
/>
{ this.state.fields.map(this.renderField.bind(this)) }
</svg>;
}
}
class Field extends React.PureComponent {
button () {
if (this.props.run === base.WON || this.props.revealed ||
(this.props.highlight && !this.props.marked)) {
return base.buttons[1];
} else if (this.props.run === base.LOST && this.props.marked && !this.props.full) {
return base.buttons[2];
} else {
return base.buttons[0];
}
}
symbol () {
if (this.props.revealed) {
return this.props.full ? base.bombs[1] : base.numbers[this.props.hint];
} else if (this.props.marked) {
return base.marks[this.props.marked];
} else if ((this.props.run > base.RUNING) && this.props.full) {
return base.bombs[0];
}
return false;
}
render () {
const button = this.button();
const symbol = this.symbol();
return [
<rect
key = "under"
x = { this.props.col * base.fieldsize }
y = { this.props.line * base.fieldsize }
width = { base.fieldsize }
height = { base.fieldsize }
className = { "under" + (symbol ? "" : " active") }
/>,
<rect
key = "fld"
x = { this.props.col * base.fieldsize + 2 }
y = { this.props.line * base.fieldsize + 2 }
rx = "3"
width = { base.fieldsize - 4 }
height = { base.fieldsize - 4 }
className = { `field ${button}` }
/>,
symbol && <use
key = "sym"
x = { this.props.col * base.fieldsize + 3 }
y = { this.props.line * base.fieldsize + 3 }
width = { base.fieldsize - 6 }
height = { base.fieldsize - 6 }
xlinkHref = { `#${symbol}` }
className = "mark"
/>
];
}
}
ReactDOM.render(<Game />, document.querySelector("#gamepad"));
Also see: Tab Triggers