<main id="app"></main>
* {
  margin: 0;
  padding: 0;
}

html,
body {
  background: #330033;
  color: #fff;
  font-family: sans-serif;
  width: 100%;
  height: 100%;
  position: relative;
}

h1 {
  font-size: 48px;
  font-weight: normal;
  line-height: 90px;
}

main {
  padding: 40px;
  background-color: #555;
  width: 360px;
  margin: 30px auto auto auto;
  text-align: center;
  border-radius: 30px;
}

button {
  display: block;
  border: 2px orangered solid;
  background: orange;
  font-size: 36px;
  color: #fff;
  border-radius: 10px;
  padding: 10px 20px;
  min-width: 70px;
  cursor: pointer;
  margin: 20px auto auto auto;
}

.counter {
  background: #111;
  width: 160px;
  margin-right: auto;
  margin-left: auto;
}

.counter h1 {
  margin-bottom: 0px;
}

.counter button {
  display: inline-block;
  border-color: cornflowerblue;
  background: aquamarine;
  color: cornflowerblue;
  padding: 5px;
  min-width: 55px;
  margin: 10px;
}

main > p {
  margin: 40px auto 40px auto;
  line-height: 48px;
  font-size: 36px;
  font-weight: bold;
  color: hotpink;
}
import { app } from "https://cdn.skypack.dev/hyperapp";
import html from "https://cdn.skypack.dev/hyperlit";

const Counter = ({ get, set, onChange }) => {
  const Init = (state) => set(state, 0);

  const Incr = (state) => [
    set(state, get(state) + 1),
    (dispatch) => dispatch(onChange, get(state) + 1)
  ];

  const Decr = (state) => [
    set(state, get(state) - 1),
    (dispatch) => dispatch(onChange, get(state) - 1)
  ];

  const view = (state) => html`
    <div class="counter">
      <h1>${get(state)}</h1>
      <button onclick=${Decr}>-</button>
      <button onclick=${Incr}>+</button>
    </div>
  `;
  return { Init, view };
};

const Game = ({ get, set, onFinish }) => {
  const OnCounterChange = (state, value) => [
    state,
    (dispatch) => dispatch(ScorePoint),
    value === 10 && ((dispatch) => dispatch(onFinish))
  ];

  const counter = Counter({
    get: (state) => get(state).counter,
    set: (state, counter) => set(state, { ...get(state), counter }),
    onChange: OnCounterChange
  });

  const ScorePoint = (state) => {
    const game = { ...get(state) };
    game.score = game.score + 1;
    return set(state, game);
  };

  const Init = (state) => [
    set(state, { score: 0 }),
    (dispatch) => dispatch(counter.Init)
  ];

  const view = counter.view;

  const score = (state) => get(state).score;

  return { Init, view, score };
};

const Flow = ({ get, set, score, game, onPlay }) => {
  const Init = (state) => set(state, "start");

  const Play = (state) => [set(state, "play"), (dispatch) => dispatch(onPlay)];
  const Finish = (state) => set(state, "finish");

  const view = (state) => {
    let mode = get(state);
    return mode === "start"
      ? html`
          <main>
            <h1>Play a game?</h1>
            <button onclick=${Play}>Sure!</button>
          </main>
        `
      : mode === "finish"
      ? html`
          <main>
            <h1>Game Over</h1>
            <p>Score: ${score(state)}</p>
            <button onclick=${Init}>Start over</button>
          </main>
        `
      : html`
          <main>
            ${game(state)}
            <p>Score: ${score(state)}</p>
            <button onclick=${Init}>Cancel</button>
          </main>
        `;
  };

  return { Init, view, Finish };
};

const game = Game({
  get: (state) => state.game,
  set: (state, game) => ({ ...state, game }),
  onFinish: () => flow.Finish
});

const flow = Flow({
  get: (state) => state.flow,
  set: (state, flow) => ({ ...state, flow }),
  game: game.view,
  score: game.score,
  onPlay: game.Init
});

app({
  init: flow.Init,
  view: (state) => flow.view(state),
  node: document.getElementById("app")
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.