doctype html

head
    title Ships!
    link(rel="stylesheet", href="style.css")
    meta(charset="UTF-8")
    meta(name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no")
    meta(name="theme-color" content="#000")

body
    .mode#pause

    each party in ['p1', 'p2']
        .mode(id=party)
            each faction in ['enemy', 'player']
                .battlefield(class=faction, class=party)
                    each line in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
                        each col, colI in ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L']
                            div(class='field-x' + (colI+1) + '-y' + line)
                                if (col === 'A')
                                    .indicator-col #{line}
                                if (line === 1)
                                    .indicator-line #{col}

                                each checkState in ['ship', 'hit']
                                    input(type="checkbox", class=checkState).check
                                    .check-helper

    .mode#start
        .battlefield.enemy
            ol
                li
                    span You are this color.
                li
                    span Your enemy is
                    span this
                    span color
                li
                    span You may place your ships as follows:
                    ul
                        li 1 x 5 blocks
                        li 2 x 4 blocks
                        li 3 x 3 blocks
                        li 4 x 2 blocks
                li.enternames
                    span Enter your names
                li
                    span Play fair
                li
                    span
                        a(href='#pause') Start

    .status
        input(type="text" placeholder="1st Player").player-name#name1
        input(type="text" placeholder="2nd Player").player-name#name2
        each party  in ['p1', 'p2']
            a.player-link.switch(href='#' + party)
        a.status-link.playpause(href='#pause') End Turn

    h1
        span Sh
        span i
        span ps
        span !
View Compiled
@gridSize: 12;
@zSea: 1;
@zShips: 1000;
@zAbove: 2000;

@seaColor: #123;
@enemyColor: #f0a;
@playerColor: #0c8;
@hitColor: #f27;

//no proper imports at codepen :(
//@import 'animations.less';
@keyframes setShip {
    0% {
        transform: scale(0, 0);
        background-color: transparent;
    }
    100% {
        transform: scale(1, 1);
        background-color: @playerColor;
    }
}

@keyframes hit {
    0% {
        transform: scale(0, 0);
        opacity: 0;
        background-color: transparent;
    }

    10% {
        transform: scale(1.2, 1.2);
        opacity: 1;
        background-color: spin(@hitColor, 40);

        box-shadow: 0 0 0 0.5em var(--shadowColor);
    }

    100% {
        transform: scale(.7, .7);
        opacity: .7;
        background-color: @hitColor;

        box-shadow: 0 0 0 0.5em var(--shadowColor);
    }
}

@keyframes miss {
    0% {
        transform: scale(0, 0);
        opacity: 1;
        background-color: lighten(@seaColor, 50);
    }
    100% {
        transform: scale(1, 1);
        opacity: .8;
        background-color: lighten(@seaColor, 10);
    }
}
//end of animations.less

//@import 'components.less';
.checkerboard(@counter) when (@counter > 0) {
    .checkerboard(@counter - 2);

    &[class^='field-'][class$='-y@{counter}'] {
        &:nth-of-type(odd) {
            background-color: transparent;

            :target & {
                background-color: darken(@seaColor, 3%);
            }
        }

        &:nth-of-type(even) {
            background-color: transparent;

            :target & {
                background-color: darken(@seaColor, 4%);
            }
        }
    }
}

.hit-obj {
    position: absolute;
    visibility: visible;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    animation: hit 1s forwards;
}

.ship-obj {
    position: absolute;
    left: 0;
    top: 0;
    width: 90%;
    height: 90%;
    border-radius: 15%;
    animation: setShip 0.5s forwards;
}

.miss-obj {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    animation: miss 1s forwards;
}
//End of components.less

body {
    --grid-measurements: 70vw;
    @media (min-aspect-ratio: 1/2) {
        --grid-measurements: 35vh;
    }
    
    margin: 0;
    min-height: 100vh;
    background-color: @seaColor;
    background-image: linear-gradient(to bottom, black 0, @seaColor 100%);
    background-repeat: no-repeat;
    overflow: hidden;
    backface-visibility: hidden;
    font-family: sans-serif;
}

.mode {
    opacity: 0;
    pointer-events: none;

    &:target,
    &#start {
        opacity: 1;
        pointer-events: all;
        z-index: 1;
    }

    &#p1, &#p2 {
        position: absolute;
        transform: translateX(0);
        opacity: 1;
        z-index: 2;
    }

    &#p1:target {
        transform: translateX(50vw);

        +#p2 {
            transform: translateX(50vw);
            z-index: 2;
        }
    }

    &#p2 {
        transform: translateX(50vw);
        z-index: 1;
    }

    &#pause:target {
        ~ #p1, ~ #p2 {
            opacity: 0;
        }
    }
}

#start {
    .battlefield {
        left: 50vw;
        display: flex;
        align-items: flex-start;
        justify-content: center;
        width: 100%;
        height: 100%;
    }

    ol {
        list-style: decimal-leading-zero;
        color: @enemyColor;
        font-weight: 700;

        li {
            margin-bottom: 1em;
            line-height: 1.5em;

            &.enternames {
                margin-bottom: 3em;
            }
        }

        span,
        ul li,
        a {
            color: @playerColor;
            font-weight: 400;
            margin-bottom: 0;
        }

        span:nth-of-type(even) {
            color: @enemyColor;
            margin: 0 .5ch;
        }
    }

    .mode:target ~ & {
        display: none;
    }
}

#pause {
    left: 0;
    position: absolute;
    display: flex;
    justify-content: space-evenly;
    align-items: center;
    width: 100%;
    height: 100%;
}

.battlefield {
    position: absolute;
    display: grid;
    grid-template-columns: repeat(@gridSize, 1fr);
    width: var(--grid-measurements);
    height: var(--grid-measurements);
    margin: 0 auto 5vw;
    border: 2px solid;
    transform: translate(-50%, 0);
    z-index: @zSea;

    &.player {
        top: calc(var(--grid-measurements) ~"+" 150px);
        border-color: transparent;

        :target & {
            border-color: @playerColor;
        }
    }

    &.enemy {
        top: 100px;
        border-color: transparent;

        :target & {
            border-color: @enemyColor;
        }
    }
}

[class^='field-'] {
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100%;
    height: 100%;
    background-color: transparent;

    :target &:nth-of-type(even) {
        background-color: darken(@seaColor, 2%);
    }

    :target &:nth-of-type(odd) {
        background-color: darken(@seaColor, 1%);
    }

    .checkerboard(@gridSize);

    [class^='indicator-'] {
        display: none;

        :target & {
            position: absolute;
            display: flex;
            justify-content: center;
            width: calc(var(--grid-measurements)~"/"@gridSize);
            height: calc(var(--grid-measurements)~"/"@gridSize);
            color: lighten(@seaColor, 10%);
            pointer-events: none;
        }

        &.indicator-line {
            top: -1.5em;
            align-items: flex-start;
        }

        &.indicator-col {
            left: -2.3em;
            align-items: center;
        }
    }
}

.check {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;

    + .check-helper {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        pointer-events: none;
    }

    &.ship {
        &:checked {
            pointer-events: none;
        }

        &:checked + .check-helper {
            :target .player & {
                &::after {
                    content: "";
                    .ship-obj; // set own ship
                }
            }

            :target .enemy & {
                &::after {
                    content: "";
                    .miss-obj; // miss enemy ship
                }
            }
        }

        &:checked ~ .hit {
            pointer-events: all;
        }
    }

    &.hit {
        position: absolute;
        top: ~"calc(-1 * (var(--grid-measurements) + 50px))";
        left: 50vw;
        width: 100%;
        height: 100%;
        pointer-events: none;

        #p2 &,
        #p1:target & {
            left: 0;
        }

        #p1:not(:target) & + .check-helper::before {
            left: 50vw;
        }

        &:checked {
            opacity: 1;
            visibility: hidden;
            pointer-events: none;

            + .check-helper {
                &::before {
                    content: "";
                    .hit-obj; // hit enemy ships
                    top: ~"calc(-1 * (var(--grid-measurements) + 50px))";
                }

                &::after {
                    content: "";
                    .hit-obj; // hit own ships
                    top: -2px;
                    left: -2px;
                }

                #p1:target &::before,
                #p1:target ~ #p2 &::after,
                #p1:not(:target) &::after,
                #p2:target &::before {
                    display: none;
                }
            }
        }

        #p1:target .battlefield.p1 &,
        #p2:target .battlefield.p2 & {
            display: none;
        }
    }
}

.status {
    position: relative;
    top: 23em;
    display: flex;
    justify-content: center;
    z-index: @zAbove;

    .status-link,
    .player-link {
        display: none;
    }

    .player-name {
        position: relative;
        padding: 3px;
        border: 1px solid @enemyColor;
        background: transparent;
        color: @playerColor;

        &::placeholder {
            color: @playerColor;
            opacity: 1;
        }

        &:first-of-type {
            margin-right: 1em;
        }
    }

    .mode#pause:target ~ & {
        top: 40vh;
        width: calc(100% ~"-" 40px);
        padding: 0 20px;
        text-align: center;
        z-index: @zAbove;

        .player-name,
        .player-link {
            position: absolute;
            display: block;
            width: 80%;
            max-width: 500px;
            height: 40px;
            margin: 0;
            padding: 0;

            &:nth-of-type(even) {
                top: 60px;
            }
        }

        .player-name {
            border: 0;
            border-bottom: 1px solid @enemyColor;
            font-size: 2em;
            font-weight: bold;
            text-align: center;
            text-transform: uppercase;
            pointer-events: none;
        }
    }

    .mode#p1:target ~ &,
    .mode#p2:target ~ & {
        display: block;
        top: 10px;

        .player-name,
        .status-link {
            display: block;
            padding: 0 10px;
            border: 0;
            font-size: 1.5em;
            font-weight: bold;
            text-transform: uppercase;
            pointer-events: none;
        }

        .status-link {
            display: inline-block;
            color: @enemyColor;
            text-decoration: none;
            pointer-events: all;
        }
    }

    .mode#p1:target ~ & #name2 {
        display: none;
    }

    .mode#p2:target ~ & #name1 {
        display: none;
    }
}

h1 {
    position: absolute;
    top: 0;
    width: 120px;
    font-size: 2em;
    text-transform: uppercase;
    text-align: center;
    color: @playerColor;
    transform: translateX(calc(50vw ~"-" 60px));
    transition: transform 0.4s ease-out;

    span:nth-of-type(even) {
        color: @enemyColor;
        text-transform: lowercase;
    }

    &::before {
        margin-right: 1ch;
        color: @enemyColor;
    }

    :target ~ & {
        transform: translateX(calc(100vw ~"-" 130px));
    }
}

View Compiled
//no js
//fork me: https://gitlab.com/iamschulz/ships
//full demo: http://playground.iamschulz.de/ships/

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.