Pen Settings

HTML

CSS

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

JavaScript

Babel includes JSX processing.

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

Packages

Add Packages

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.

Behavior

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.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <div id="languageChoice">
    <span id="de" title="Deutsch">DE</span> 
    <span id="en" title="English">EN</span> 
    <span id="eo"  class="currLanguage" title="Esperanto">EO</span> 
    <span id="es" title="Español">ES</span> 
    <span id="fr" title="Française">FR</span> 
    <span id="pt" title="Português">PT</span>
    <span id="sw" title="Kiswahili">SW</span> 
    <span id="tr" title="Türkçe">TR</span>
</div>
<!--
<div id="dateSelector">
    <p id="selectorHeading" class="clickable">Ŝanĝi la daton</p>
    <div id="toBeRevealed" class="hide">
        <form id="dateForm">
            <label for="year">Jaro:</label>
            <select type="text" id="year">
                <option value="2020">2020</option>
                <option value="2021">2021</option>
                <option value="2022">2022</option>
                <option value="2023">2023</option>
                <option value="2024">2024</option>
                <option value="2025">2025</option>
                <option value="2026">2026</option>
                <option value="2027">2027</option>
                <option value="2028">2028</option>
                <option value="2029">2029</option>
                <option value="2030">2030</option>
            </select>
            <label for="month">Monato:</label>
            <select type="text" id="month">
                <option value="0" data-long>Jan</option>
                <option value="1">Feb</option>
                <option value="2" data-long>Mar</option>
                <option value="3">Apr</option>
                <option value="4" data-long>Maj</option>
                <option value="5">Jun</option>
                <option value="6" data-long>Jul</option>
                <option value="7" data-long>Aŭg</option>
                <option value="8">Sep</option>
                <option value="9" data-long>Okt</option>
                <option value="10">Nov</option>
                <option value="11" data-long>Dec</option>
            </select>
            <label for="day">Tago:</label>
            <select id="day">
                <option value="1">1</option>
                <option value="2">2</option>
                <option value="3">3</option>
                <option value="4">4</option>
                <option value="5">5</option>
                <option value="6">6</option>
                <option value="7">7</option>
                <option value="8">8</option>
                <option value="9">9</option>
                <option value="10">10</option>
                <option value="11">11</option>
                <option value="12">12</option>
                <option value="13">13</option>
                <option value="14">14</option>
                <option value="15">15</option>
                <option value="16">16</option>
                <option value="17">17</option>
                <option value="18">18</option>
                <option value="19">19</option>
                <option value="20">20</option>
                <option value="21">21</option>
                <option value="22">22</option>
                <option value="23">23</option>
                <option value="24">24</option>
                <option value="25">25</option>
                <option value="26">26</option>
                <option value="27">27</option>
                <option value="28">28</option>
                <option value="29" id="leapyear">29</option>
                <option value="30">30</option>
                <option value="31" class="longMonths"></option>
           </select>
            <button id="submit" type="submit" class="rounded">Ek!</button>
        </form>
        <p id="back" class="clickable">Montru hodiaŭ</p>
    </div>
</div>
-->
<canvas id="clock" style="position: absolute; z-index: 1; left:0; top: 0;">
</canvas>
<div id="corona" class="circle">
    <div id="sun" class="circle"></div>
</div>
<div id="earth" class="circle" height="35" width="35">
    <div id="moon" class="circle" height="15" width="15"></div>
</div>
<div id="display"></div>

              
            
!

CSS

              
                body {
    background-color: black;
    padding: 0;
    margin: 0;
    box-sizing: border-box !important;
    font-family: 'Helvetica', 'Arial', 'sans-serif';
    font-size: 14px;
}
#clock {
   // Canvas' starting point is at the 3 o'clock position, we shift it to the top. 
   transform: rotate(-90deg); 
}
.circle {
    border-radius : 50%;
}
#earth {
    height: 35px;
    width: 35px;
    background: linear-gradient(0deg, cyan, black 22px); // initial angle, further adjusted in js.
    position: absolute;
    z-index: 10;
}

#moon {
    height: 15px;
    width: 15px;
    background: linear-gradient(0deg, white, black 10px); // initial angle, further adjusted in js.
    position: absolute;
    z-index: 12;
    left: 10px; // centered on earth, prior to getting orbital angle
    top: 10px;
}
#corona {
    position: absolute;
    top: 325px;
    left: 325px;
    z-index: 5;
    height: 150px;
    width: 150px;
    background: radial-gradient(circle, #fc0, black 75px);
}

#sun {
    position: absolute;
    top: 25px;
    left: 25px;
    width: 100px;
    height: 100px;
    background: radial-gradient(circle, white, #fed 75px);
}
#textCanvas {
    position: absolute;
    top: 120px;
    left: 120px;
    z-index: 110;
    //border: 1px #333 dotted;
}
#display {
    background-color: rgba(0,0,0,.4);
    width: 300px;
    height: 40px;
    padding:10px;
    text-align: center;
    color: rgba(203, 223, 255, .75);
    position: absolute;
    top: 260px;
    left: 250px;
    z-index: 13
}
#languageChoice {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 15;
    color: #fff;
    padding: 20px;
    color: rgba(203, 223, 255, .5);
    font-size: 12px;
}
#languageChoice span {
    font-size: 10px;
    color: #fff;
    cursor: pointer;
}
.currLanguage {
    margin-bottom: 0;
    color: #ff0 !important;
    text-decoration: underline;
}
#dateSelector {
    position: absolute;
    z-index: 20;
    top: 40px;
    left: 20px;
}
#dateSelector, #dateSelector label, #dateSelector select, #dateSelector button {
    font-size: 12px;
    font-weight: normal !important;
}
#dateSelector {
    color: white;        
}
#dateSelector select, #dateSelector button {
    margin-left: .5rem;
    margin-right: .75rem;
    font-weight: lighter;
    border: 0;
}
.rounded {
    border-radius: 15px;
    padding:  .15rem .5rem;
}
.clickable {
    cursor: pointer;
    color: cyan;
}
.hide {
    display: none;
}
.show {
    display: block; 
}
              
            
!

JS

              
                // math
const tau = 2 * Math.PI // At least for canvas, tau is infinitely easier than pi.
const degreesToRadians = deg => tau / 360 * deg
// dates
let now = new Date;
const options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };

let formattedDate = `${now.toLocaleDateString('en-US', options)}.`
/*
const getEnteredDate = function(ev) { // event is 'submit' called in enableForm() below.
    const year =  document.getElementById('year').value
    const month = document.getElementById('month').value
    const day = document.getElementById('day').value
    const setDate = Date.UTC(year, month, day)
    now = new Date(setDate)
    drawDays()
    ev.preventDefault();
 }
 */
const year = now.getUTCFullYear()
const jan1 = new Date(Date.UTC(year, 0, 1))
const jan1Day = jan1.getDay() // for days of the week
const numOfDays = year % 4 !== 0 || year % 100 === 0 ? 365 : 366
const today = function daysIntoYear(d) { 
    return (( Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()) - Date.UTC(now.getFullYear(), 0, 0)) / 24 / 60 / 60 / 1000) // this is how far are we into the year today
}
// colors
const white = 'white';
const gold = 'rgb(255, 204, 51)'
const subtleBlue = 'rgba(0, 204, 204, .5)'
const blue = 'rgb(0, 204, 204)'
const purple = 'rgb(153, 0, 204)'
const green = 'rgb(0, 255, 51)'

// Easter daysIntoYear for 2020-2029
const easters = {
    2020: 103,
    2021: 94,
    2022: 107,
    2023: 99,
    2024: 91,
    2025: 110,
    2026: 95,
    2027: 87,
    2028: 107,
    2029: 91
}
// first days of each month in common years or leap years
const firstDays =
    numOfDays === 365 ? [1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]
        : [1, 32, 61, 92, 122, 153, 183, 214, 245, 275, 306, 336]

// elements
const corona = document.getElementById('corona')
const sun = document.getElementById('sun')
const earth = document.getElementById('earth')
const display = document.getElementById('display')
//const heading = document.getElementById('selectorHeading')
//const form = document.getElementById('dateForm');

// the clock canvas
const cl = document.getElementById('clock')
cl.width = 800
cl.height = 800
const cx = cl.width / 2 //  center coordinates
const cy = cl.height / 2
const rad = cl.height * 0.3  // work out the radius
const mrad = rad / 2
const c = cl.getContext('2d')
c.lineWidth = 1

// utils
const containsClass = function (element, className) {
    if (element) {
        return (' ' + element.className + ' ').indexOf(' ' + className + ' ') > -1
    } else {
        return false
    }
}
const toggleClass = function (element, className) {
    if (element) {
        (containsClass(element, className) ? removeClass : addClass)(element, className);
    }
}
const addClass = function (element, className) {
    if (element) {
        containsClass(element, className) ? false : (element.className += ' ' + className)
    }
}
const removeClass = function (element, className) {
    if (element) {
        element.className = (' ' + element.className + ' ').replace(' ' + className + ' ', ' ')
    }
}
const dayArc = tau / numOfDays
const showDate = function () { display.textContent = `${formattedDate}` }

// draw functions
const drawSun = function(corona) {
    console.log(corona);
    corona.style.top = '325px'
    corona.style.left = '325px'
}

// Since the earth isn't an object in the canvas, its coordinates are computed separately.
const drawEarth = function (earth, angle, tau) {
    const ew = earth.getAttribute('width')
    // center point
    const eCtrX = cx - ew / 2
    const eCtrY = cy - ew / 2

    // X & Y polar formulas reversed here because calendar is rotated left tau/4 radians. (90 degrees)
    const ex = eCtrX + Math.sin(angle - tau / 4) * rad * 1.35
    const ey = eCtrY + Math.cos(angle - tau / 4) * rad * 1.35
    // console.log(ex, ey, eCtrX, eCtrY)
    earth.style.top = `${ex}px`
    earth.style.left = `${ey}px`
    earth.style.transform = `rotate(${ angle * 360/tau }deg)`;
}

// builds on the coordinates of drawEarth
const drawMoon = function(angle) {                // place the moon in its proper orbital position
    const seconds = now.getTime() / 1000 - 592500 // seconds since the first new moon of the epoch
    const phase = 2551442.8769                    // length of a synodic month
    const moonAge = () => seconds % 2551442.8769 / phase
    const moonAngle = () => moonAge() * tau      //angle in radian
    const mCtrX = 9.5
    const mCtrY = 9.5
    const moonRad = 45
    const mx = mCtrX + Math.cos(moonAngle()) * moonRad
    const my = mCtrY + Math.sin(moonAngle()) * moonRad
    moon.style.top = `${mx}px`
    moon.style.left = `${my}px`
}
/*
function drawGrid() {
    c.beginPath()
    c.strokeStyle = 'rgb(51,0,0)'

    for (let i = 1; i <= 4; i++) {
        let angle = i * tau / 4
        let x = cx + Math.cos(angle) * rad
        let y = cy + Math.sin(angle) * rad
        c.moveTo(cx, cy)
        c.lineTo(x, y)
        c.stroke()
        c.closePath()
    }
}
*/
function drawMonths(angle, monEndX, monEndY) {
    radAdj = 0.5
    monStartX = cx + Math.cos(angle) * rad * radAdj
    monStartY = cy + Math.sin(angle) * rad * radAdj
    c.beginPath()
    c.strokeStyle = subtleBlue;
    c.moveTo(monStartX, monStartY)
    c.lineTo(monEndX, monEndY)
    c.stroke()
    c.closePath()
}

function drawDays() {
    let day = (jan1Day + 1) % 7 // +1 because days are shifted in loop below
    let dayOfMonth
    c.clearRect(0,0, 800, 800)
    for (let i = 1; i <= numOfDays; ++i) {
        let angle = (i - 1) * dayArc // -1 because there are no '0' days in the month
        let radAdj = i === today() ? 0.92 : 1 // indent the 'today' line
        let endLen = i === today() ? 4.8 : 1 // outdent the 'today' line
        let dayStartX = cx + Math.cos(angle) * rad * radAdj
        let dayStartY = cy + Math.sin(angle) * rad * radAdj
        let dayEndX = dayStartX + Math.cos(angle) * 10 * endLen
        let dayEndY = dayStartY + Math.sin(angle) * 10 * endLen
        let yearStr = year.toString()
        if (firstDays.includes(i)) {
            drawMonths(angle, dayStartX, dayStartY)
            dayOfMonth = 1
        }

        c.beginPath()
        if (i === today()) {
            c.strokeStyle = white
        } else if (
            (numOfDays === 365 && i === 359) ||
            (numOfDays === 366 && i === 360) ||
            (i === 1)
        ) {
            // Christmas (Western)
            c.strokeStyle = green
        } else if (
            Object.keys(easters).includes(yearStr) &&
            easters[yearStr] === i
        ) {
            // Easter (Western)
            c.strokeStyle = green
        } else if (day === 6 || day === 0) {
            // Weekends in purple
            c.strokeStyle = purple
        } else {
            c.strokeStyle = gold
        }

        c.moveTo(dayStartX, dayStartY)
        // longer ticks for the 10th, 20th, and 30th of each month
        if (dayOfMonth % 10 === 0) {
            c.lineTo(
                dayStartX + Math.cos(angle) * 15 * endLen,
                dayStartY + Math.sin(angle) * 15 * endLen
            )
        } else {
            c.lineTo(dayEndX, dayEndY)
        }
        c.stroke()
        c.closePath()

        if (i === today()) {
            drawEarth(earth, angle, tau)
            drawMoon(); 
        }
        day = (day + 1) % 7
        dayOfMonth += 1
    }
    showDate()
}

// Thanks to graphicsgen for the following function
// http://blog.graphicsgen.com/2015/03/html5-canvas-rounded-text.html
function getCircularText(text, dist, startAngle, align, textInside, inwardFacing, fName, fSize, kerning) {
    // declare and intialize canvas, reference, and useful variables
    align = align.toLowerCase()
    var textCanvas = document.createElement('canvas')
    textCanvas.setAttribute('id', 'textCanvas')

    var t = textCanvas.getContext('2d')
    var clockwise = align == 'right' ? 1 : -1 // draw clockwise for aligned right. Else Anticlockwise
    startAngle = startAngle * (tau / 360) // convert to radians
    var textHeight = 70
    textCanvas.width = cl.width
    textCanvas.height = cl.width
    t.fillStyle = blue
    t.font = fSize + ' ' + fName
    // Setup letters and positioning
    t.translate(rad + 40, rad +40) // Move to center
    startAngle += tau / 2 * !inwardFacing  // Rotate 180 if outward facing
    t.textBaseline = 'middle' // Ensure we draw in exact center
    t.textAlign = 'center'

    if (
        (['left', 'center'].indexOf(align) > -1 && inwardFacing) ||
        (align == 'right' && !inwardFacing)
    )
        text = text
            .split('')
            .reverse()
            .join('')
    // Phew... now rotate into final start position
    t.rotate(startAngle)

    // Now for the fun bit: draw, rotate, and repeat
    for (var j = 0; j < text.length; j++) {
        var charWid = t.measureText(text[j]).width // half letter
        // rotate half letter
        t.rotate(charWid / 2 / (rad - textHeight) * clockwise)
        // draw the character at "top" or "bottom"
        // depending on inward or outward facing
        t.fillText(
            text[j],
            0,
            (inwardFacing ? 1 : -1) * (0 - rad + textHeight / 2)
        )
        t.rotate(
            (charWid / 2 + kerning) / (rad - textHeight) * clockwise
        ) // rotate half letter
    }
    return textCanvas
}
function getLanguage() {
    const curr = document.querySelector('.currLanguage') 
    return  curr.id
}
function enableLangSwitcher() {
    const langs = document.querySelectorAll('#languageChoice span')
    langs.forEach(function (el) {
        el.addEventListener('click', function() {setLanguage(el)}, false)
    })
}
function setMonthNames(id) {
    const monthNamesde = `  JAN    FEB    MAR    APR    MAI    JUN    JUL    AUG    SEP    OKT    NOV    DEZ  `
    const monthNamesen = `  JAN    FEB    MAR    APR    MAY    JUN    JUL    AUG    SEP    OCT    NOV    DEC  `
    const monthNameseo = `  JAN    FEB    MAR    APR    MAJ    JUN    JUL    AŬG    SEP    OKT    NOV    DEC  `
    const monthNameses = `  ENE    FEB    MAR    ABR    MAY    JUN    JUL    AGO    SET    OCT    NOV    DIC  `
    const monthNamesfr = `  JAN    FÉV    MAR    AVR    MAI    JUN    JUL    AOÛ    SEP    OCT    NOV    DÉC  `
    const monthNamespt = `  JAN    FEV    MAR    ABR    MAI    JUN    JUL    AGO    SET    OUT    NOV    DEZ  `
    const monthNamessw = `  JAN    FEB    MAC    APR    MEI    JUN    JUL    AGO    SEP    OKT    NOV    DES  `
    const monthNamestr = `  OCA    ŞUB    MAR    NİS    MAY    HAZ    TEM    AĞU    EYL    EKİ    KAS    ARA  `
    switch(id) {
        case 'de': { // German: 5th most-widely-spoken Latin-script language.
          return monthNamesde
        } case 'en': { // English: most-widely-spoken Latin-script language.
            return monthNamesen
        } case 'eo': // Esperanto
        default: { 
            return monthNameseo
        } case 'es': { // Spanish: 2nd most-widely-spoken Latin-script language.
            return monthNameses
        } case 'fr': { // French: 3rd most-widely-spoken Latin-script language.
            return monthNamesfr
        } case 'pt': { // Portuguese: 4th most-widely-spoken Latin-script language.
            return monthNamespt
        } case 'sw': { // Swahili: 6th most-widely-spoken Latin-script language.
            return monthNamessw
        } case 'tr': { // Turkçe: 7th most-widely-spoken Latin-script language.
            return monthNamestr
        }
    }
}
function setLanguage(el) {
    const prev = document.getElementById(getLanguage())
    removeClass(prev, 'currLanguage')
    const id = el.id 
    addClass(el, 'currLanguage')
    const months = setMonthNames(id)
    const del = document.getElementById('textCanvas') || void 0
    if (del) del.remove()
    document.body.appendChild(
        getCircularText(
            months,
            rad,
            0,
            'right',
            true,
            true,
        'Courier New',
        '17px',
        2.50
        )
    )
}
/*
const toggleDateSelectorText = function () {
    const revealed = document.getElementById('toBeRevealed');
    toggleClass(revealed, 'hide')
    const back = document.getElementById('back');
    back.addEventListener('click', function () {
        toggleDateSelectorText();
        drawDays();
    } , false);    
}
*/
//const enableForm = () => form.addEventListener('submit', getEnteredDate);

// Let's get the party started!
enableLangSwitcher()
//drawGrid()
//enableForm();
drawDays() // calls drawMonths(), drawEarth() and showDate()
document.body.appendChild(
    getCircularText(
        setMonthNames(getLanguage()),
        rad,
        0,
        'right',
        true,
        true,
        'Courier New',
        '17px',
        2.50
    )
)
//heading.addEventListener('click', toggleDateSelectorText , false)


              
            
!
999px

Console