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

              
                section.speedometer-container
    .speedometer
        .inner-ring
        .outer-ring
            - var i = 0
            while i++ < 49
                span.tick
        .digit-ring
            - var i = 0
            while i++ < 9
                span.digit= (i - 1) * 20
        .details
            p.label Download
            p.speed 87.3
            p.unit Mbps
        .progress
        button.retry-button Retry
        footer
            .stat
                label Ping
                p 2ms
            .stat
                label Upload
                p 67.7 Mbps
    .overlay
              
            
!

CSS

              
                /** Function Definitions **/
_position(position, top, right = top, bottom = top, left = right)
    position position
    top top
    left left
    right right
    bottom bottom


/** Basic Styles **/
*
*::before
*::after
    margin 0
    padding 0
    box-sizing border-box


html
    font-size 62.5%


body
    display flex
    justify-content center
    align-items center
    height 100vh
    font-family "Roboto", sans-serif
    -webkit-font-smoothing antialiased
    -moz-osx-font-smoothing grayscale
    line-height 1
    color #666
    user-select none


/** Speedometer Styles **/
.speedometer-container
    width 48rem
    height 48rem
    position relative
    background-color #000
    box-shadow 0 0 10rem rgba(0, 0, 0, 0.75)

    &::before
        content ""
        _position(absolute, 0)
        background-image radial-gradient(circle at top left, #fff, transparent)
        opacity 0.12


.overlay
    pointer-events none

    &::before,
    &::after
        content ""
        _position(absolute, 0)

    &::before
        background-image radial-gradient(circle at top left, #25fabb, transparent)
        opacity 0.04

    &::after
        background-image radial-gradient(circle at bottom right, #874bd7, transparent)
        opacity 0.2


.speedometer .inner-ring
    width 21rem
    height 21rem
    position absolute
    top calc(50% - 12.5rem)
    left calc(50% - 10.5rem)

    &::before
    &::after
        content ""
        _position(absolute, 0)
        border 2px solid transparent
        border-top 2px solid #3b3d45
        border-right 2px solid #3b3d45
        border-radius 50%

    &::before
        transform rotate(-75deg)

    &::after
        transform rotate(-15deg)


.speedometer .outer-ring
    width 32rem
    height 32rem
    position absolute
    top calc(50% - 18rem)
    left calc(50% - 16rem)
    border-radius 50%


.speedometer .digit-ring
    position absolute
    top calc(50% - 2rem)
    left 50%


.speedometer .tick
    width 0.8rem
    border-top 2px solid #3b3d45
    position absolute
    top calc(50% - 0.1rem)
    left calc(50% - 0.4rem)

    &:nth-child(6n+1)
        width 1.6rem
        left calc(50% - 0.8rem)
        border-color #787a81


.speedometer .digit
    width 2rem
    height 2rem
    position absolute
    top calc(50% - 1rem)
    left calc(50% - 1rem)
    font-weight bold
    text-align center
    line-height 2rem


.speedometer .details
    display flex
    flex-direction column
    justify-content center
    align-items center
    width 21rem
    height 21rem
    position absolute
    top calc(50% - 12.5rem)
    left calc(50% - 10.5rem)


.speedometer .label
    font-size 1.2rem
    font-weight bold
    text-transform uppercase


.speedometer .speed
    font-size 6rem
    color #fff


.speedometer .unit
    font-size 1.6rem


.speedometer .progress
    width 21rem
    height 21rem
    position absolute
    top calc(50% - 12.5rem)
    left calc(50% - 10.5rem)
    border-radius 50%

    &::before
        content ""
        position absolute
        top -0.2rem
        left calc(50% - 0.3rem)
        width 0.6rem
        height 0.6rem
        border-radius 50%
        background-color #f1252e
        box-shadow 0 0 6rem 2rem rgba(241, 37, 46, 0.35)


.speedometer .retry-button
    width 10rem
    border 2px solid #3b3d45
    appearance none
    position absolute
    left calc(50% - 5rem)
    bottom 13.5rem
    font-size 1.2rem
    font-weight bold
    text-align center
    text-transform uppercase
    line-height 3rem
    color #666
    border-radius 3rem
    background-color transparent
    cursor pointer
    outline none
    transition background-color 250ms ease-out

    &:hover
    &:focus
        background-color rgba(59, 61, 69, 0.15)


.speedometer footer
    display flex
    justify-content center
    padding 3.5rem 0
    _position(absolute, auto, 0, 0)


.speedometer .stat
    flex-grow 1
    width 0
    padding 1rem 0
    text-align center

.speedometer .stat:not(:last-child)
    border-right 2px solid rgba(255, 255, 255, 0.05)


.speedometer .stat label
    display block
    margin-bottom 0.75rem
    font-size 1.2rem
    font-weight bold
    text-transform uppercase


.speedometer .stat p
    font-size 1.4rem
    color #fff

              
            
!

JS

              
                var ticks = $('.tick');
var digits = $('.digit');
var details = $('.details');
var progress = $('.progress');

var outerRingRadius = 164;
var digitRingRadius = 145;


ticks.each(function(i) {
    var angle = 210 - i * 5;
    var theta = deg2rad(angle);
    var radius = outerRingRadius + (i % 6 ? 0 : 4);
    var x = Math.cos(theta) * radius;
    var y = Math.sin(theta) * -radius;
    var transform = [
        `translate(${ x }px, ${ y }px)`,
        `rotate(${ -angle }deg)`
    ].join(' ');
    $(this).css({
        '-webkit-transform': transform,
        '-moz-transform': transform,
        'transform': transform
    });
});

digits.each(function(i) {
    var angle = 210 - i * 30;
    var theta = deg2rad(angle);
    var x = Math.cos(theta) * digitRingRadius;
    var y = Math.sin(theta) * -digitRingRadius;
    $(this).css({
        '-webkit-transform': `translate(${ x }px, ${ y }px)`,
        '-moz-transform': `translate(${ x }px, ${ y }px)`,
        'transform': `translate(${ x }px, ${ y }px)`
    });
});

$('.retry-button').on('click', function() {
    statValueCurrent = 0;
    updateDetails();
});


var frameCount = 100;
var frameInterval = 0.3;
var digitValueMax = 160;
var statValueMax = 87.3;
var statValueCurrent = 0;
var statValueInterval = statValueMax / frameCount;

updateDetails();


function updateDetails() {
    if (statValueCurrent.toFixed(1) > statValueMax) {
        return;
    }
    setStatValue(statValueCurrent.toFixed(1));
    statValueCurrent += statValueInterval;
    setTimeout(updateDetails, frameInterval);
}


function setStatValue(value) {
    var angle = -120 + 240 * (value / digitValueMax);
    progress.css({
        'transform': `rotate(${ angle }deg)`
    });
    details.find('.speed').text(value);
}


function deg2rad(angle) {
    return angle * (Math.PI / 180);
}


function rad2deg(angle) {
    return angle * (180 / Math.PI);
}

              
            
!
999px

Console