Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

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.

+ 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

Auto Save

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

              
                html
  head
  body.demo-1.loading.cda-alignright.cda-naked
    main
      .page.page--layout-2(data-scroll='')
        .content.content--full.content--alternate
          .content__item.content__item--wide
            span.content__item-number 11
            .content__item-imgwrap
              .content__item-img(style='background-image: url(https://www.lacor.info/accueil/galerie-livres/img/2007_tours_de_roles.png);')
            .content__item-deco
            h2.content__item-title 2007
            p.content__item-description Acteurs et actrices d'un film à l'autre
          
          .content__item.content__item--wide
            span.content__item-number 10
            .content__item-imgwrap
              .content__item-img(style='background-image: url(https://www.lacor.info/accueil/galerie-livres/img/2004_prejuge_de_la_rampe.png);')
            .content__item-deco
            h2.content__item-title 2004
            p.content__item-description Pour un cinéma déchaîné
          
          .content__item.content__item--t3--wide
            span.content__item-number 09
            .content__item-imgwrap
              .content__item-img(style='background-image: url(https://www.lacor.info/accueil/galerie-livres/img/2003_mecanic_cinema.png);')
            .content__item-deco
            h2.content__item-title 2003
            p.content__item-description Technologies, machines, outils, objets divers
          
          .content__item.content__item--t2--wide
            span.content__item-number 08
            .content__item-imgwrap
              .content__item-img(style='background-image: url(https://www.lacor.info/accueil/galerie-livres/img/2002_derriere_la_porteII.png);')
            .content__item-deco
            h2.content__item-title 2002
            p.content__item-description Pouvoirs du secret au cinéma
          
          .content__item.content__item--t1--wide
            span.content__item-number 07
            .content__item-imgwrap
              .content__item-img(style='background-image: url(https://www.lacor.info/accueil/galerie-livres/img/2001_derriere_la_porteI.png);')
            .content__item-deco
            h2.content__item-title 2001
            p.content__item-description Secrets de famille au cinéma
          
          .content__item.content__item--wide
            span.content__item-number 06
            .content__item-imgwrap
              .content__item-img(style='background-image: url(https://www.lacor.info/accueil/galerie-livres/img/2000_epreuve_du_monde.png);')
            .content__item-deco
            h2.content__item-title 2000
            p.content__item-description Entre réel et fiction
          
          .content__item.content__item--wide
            span.content__item-number 05
            .content__item-imgwrap
              .content__item-img(style='background-image: url(https://www.lacor.info/accueil/galerie-livres/img/1999_raison_en_feu.jpg);')
            .content__item-deco
            h2.content__item-title 1999
            p.content__item-description La fascination du cinéma pour la folie
          
          .content__item.content__item--wide
            span.content__item-number 04
            .content__item-imgwrap
              .content__item-img(style='background-image: url(https://www.lacor.info/accueil/galerie-livres/img/1998_je_est_un_film.png);')
            .content__item-deco
            h2.content__item-title 1998
            p.content__item-description Autour de l'autobiographie
            
            
          .content__item.content__item--wide
            span.content__item-number 03
            .content__item-imgwrap
              .content__item-img(style='background-image: url(https://www.lacor.info/accueil/galerie-livres/img/1997_plaisir_des_larmes.png);')
            .content__item-deco
            h2.content__item-title 1997
            p.content__item-description Autour du mélo
            
          .content__item.content__item--wide
            span.content__item-number 02
            .content__item-imgwrap
              .content__item-img(style='background-image: url(https://www.lacor.info/accueil/galerie-livres/img/1996_violences_du_cinema.jpg);')
            .content__item-deco
            h2.content__item-title 1996
            p.content__item-description 
            
          .content__item.content__item--wide
            span.content__item-number 01
            .content__item-imgwrap
              .content__item-img(style='background-image: url(https://www.lacor.info/accueil/galerie-livres/img/1995_cinema_meurtri.jpg);')
            .content__item-deco
            h2.content__item-title 1995
            p.content__item-description Un siècle de censure au cinéma
            
            
          p.credits
            | Éditions de l'
            a(href='https://www.lacor.info/cinemas-ouest/index.php/livres/livres-edites-par-l-acor', target="_top") ACOR 
    script(src='https://www.lacor.info/accueil/galerie-livres/js/imagesloaded.pkgd.min.js')
    script(src='https://www.lacor.info/accueil/galerie-livres/js/demo.js')
    script(src='https://www.lacor.info/accueil/galerie-livres/js/TweenMax.min.js')

              
            
!

CSS

              
                *,
*::after,
*::before
    box-sizing border-box

:root
    font-size 16px
    
$scroll--shadow=rgba(5,5,5,0.2)
$scroll--bg=hsla(270,47%,10%,0)
$scroll--bar=rgba(5,5,5,0.5)

body
    margin 0
    --color-text white
    --color-bg transparent
    --color-link hsla(270,47%,40%,.7)
    --color-link-hover white
    --aspect-ratio 1/1.5
    --size-title 10rem
    --font-weight-title 400
    color var(--color-text)
    background-color var(--color-bg)
    background-repeat no-repeat
    background-position center 2vh
    background-size 5%
    font-family 'Pt sans narrow', sans-serif
    font-size 1.5rem
    -webkit-font-smoothing antialiased
    -moz-osx-font-smoothing grayscale
    animation:blink 2s  cubic-bezier(1,2.33,1,-2.33) 4 backwards
    &.cda-naked
        #cdawrap
            top 5vw
            right 5vw
            --cda-text-color var(--color-link)
            --cda-text-weight inherit
            font-size 14px
            --cda-width 220px
            padding 0
            
            
 @keyframes blink
   to
    background-image url(https://www.lacor.info/accueil/galerie-livres/img/arrow-16-512.png)
   
     
::-webkit-scrollbar-track  
  -webkit-box-shadow  inset 0 0 1px $scroll--shadow
  background-color $scroll--bg 
  border-radius  10px 

::-webkit-scrollbar  
  width 5px 
  background-color  $scroll--bg

::-webkit-scrollbar-thumb 
  border-radius: 10px
  background-color $scroll--bg 
  background-image -webkit-gradient(linear, 40% 0%, 75% 84%, from($scroll--shadow), to($scroll--shadow), color-stop(0.6, $scroll--bar))

  
.demo-1
    --color-text hsl(240,51%,31%)
    --color-bg hsl(240,51%,21%)
    --color-bg rgba(5,5,5,0.2)
    --color-link-hover #5c4541
    font-size 1.25rem
    font-family ivymode, sans-serif
    --size-title 10vw
    --blendmode-title overlay
    --font-title ivymode
    --aspect-ratio 1/1.5
    --imgwidthmax 35vw
    .content__item-imgwrap
        outline 1px solid hsl(240,21%,11%)
        box-shadow 0 0 10px 0 hsl(240,41%,11%)

.js
    .loading
        &::before
            content ''
            position fixed
            z-index 100000
            top 0
            left 0
            width 100%
            height 100vh
            background var(--color-bg)
        &::after
            content ''
            position fixed
            z-index 100000
            top 50%
            left 50%
            width 60px
            height 60px
            margin -30px 0 0 -30px
            pointer-events none
            border-radius 50%
            opacity 0.4
            background var(--color-link)
            animation:loaderAnim 0.7s linear infinite alternate forwards

@keyframes loaderAnim
      to 
		    opacity 1
        transform scale3d(0.5,0.5,1)

a
   text-decoration none
   color var(--color-link)
   outline none

a:hover,
a:focus
    color var(--color-link-hover)
    outline none

.page
    display grid
    padding 5vw
    max-width 700px
    margin 0 auto
    grid-template-columns 100%
    grid-template-areas 'header' 'meta' 'grid'
    will-change transform

.page__title
    grid-area header
    margin 0 0 1rem
    font-size inherit
    font-weight 400

.credits
    text-align center

.credits--fixed
    position fixed
    bottom 1vh
    margin 0
    left 5vw
    width 90vw
    text-align right
    &::before
        content ''
        background currentColor
        position absolute
        left 0
        bottom 0
        height 1rem
        width 1px

.meta
    grid-area meta

.meta--center
    justify-self center
    padding-top 20vh

.meta__links
    display flex
    flex-wrap wrap
    line-height 1.5
    a
        margin 0 1.5rem 0 0

.meta__links--column
    flex-direction column

.meta__demos
    margin-top 1rem

.meta__demo
    display inline-block
    margin-right 0.75rem

.meta__demo--current
    color var(--color-link-hover)
    pointer-events none

.content
    grid-area grid
    margin 50vh 0 2vh

.content--offset
    display grid
    grid-template-columns repeat(2, minmax(100px, 1fr))
    grid-gap 30vh 10vw
    .content__item
        margin 0 auto 15vh
        &:nth-child(even)
            align-self end
            margin-bottom -20vh

.content--full
    width 70vw
    justify-self center

.content--padded
    padding 0 10vw

.content__item
    --imgwidth calc(var(--imgwidthmax) * var(--aspect-ratio))
    width var(--imgwidth)
    max-width 100%
    position relative
    will-change transform
    margin-bottom 32vh
    display grid
    grid-template-columns 50% 50%
    &:nth-child(even)
        .content__item-number
            right auto
            left -20vw
    &:nth-child(odd)
        .content__item-number
            left auto
            right -20vw

.content__item--wide
    grid-template-columns 20% 80%
    &:nth-child(even)
        grid-template-columns 80% 20%

.content--center
    .content__item
        margin 0 auto 60vh
    .content__item-title
        grid-area 1 / 1 / 3 / 3
        margin auto

.content__item--expand
    width 100%
    grid-template-columns minmax(0, var(--imgwidth)) 1fr
    &:nth-child(even)
        grid-template-columns 1fr minmax(0, var(--imgwidth))
        .content__item-imgwrap
            grid-area 1 / 2 / 3 / 3
            justify-self end
        .content__item-description
            grid-area 1 / 1 / 3 / 2
            justify-self end
            
    .content__item-imgwrap
        grid-area 1 / 1 / 3 / 2
    .content__item-description
        grid-area 1 / 2 / 3 / 3
        justify-self start
        text-align center
        align-self start
        padding 0 2rem
        width 250px
        font-size 0.9rem

.content--alternate
    .content__item
        max-width 90vw
        &:nth-child(even)
            margin-left auto

.content__item-imgwrap
    position relative
    --imgwidth 100%
    margin 0 auto
    grid-area 1 / 1 / 3 / 3
    overflow hidden
    width var(--imgwidth)
    padding-bottom calc(var(--imgwidth) / (var(--aspect-ratio)))
    will-change transform

.content__item-img
    --overflow 40px
    height calc(100% + (2 * var(--overflow)))
    top calc( -1 * var(--overflow))
    width 100%
    position absolute
    background-size cover
    background-repeat no-repeat
    background-position 50% 150%
    will-change transform
    opacity 0.8

.content__item-img--t1
    --overflow 60px

.content__item-img--t2
    --overflow 80px

.content__item-img--t3
    --overflow 120px

.content__item-number
    opacity 0.07
    font-size 25vw
    position absolute
    top -7vw
    right -10vw
    line-height 1

.content__item-title
    position relative
    font-size var(--size-title)
    padding 0 3vw
    margin calc(var(--size-title) * -1/2) 0 0 0
    align-self start
    line-height 1
    font-family var(--font-title)
    font-weight var(--font-weight-title)
    color var(--color-title)
    will-change transform
    mix-blend-mode var(--blendmode-title)

.content__item-title--layer
    padding 0
    z-index 10
    grid-area 1 / 1 / 2 / 3
    width 100%
    text-align center

.content__item-description
    grid-area 3 / 1 / 3 / 3
    width 100%
    position relative
    margin 0
    padding 1rem 0 0 0
    color var(--color-description)

.content--alternate .content__item-title,
.content__item--wide:nth-child(even) .content__item-description
    grid-area 3 / 1 / 4 / 2
    justify-self start

.content--alternate  .content__item:nth-child(even) .content__item-title,
.content__item--wide .content__item-description
    grid-area 3 / 2 / 4 / 3
    justify-self end
    width auto

.content__item-deco
    position absolute
    height 30%
    width 0
    right -10vw
    line-height 1
    background blue
    opacity 0.07

.content__item-decobar
    width 140%
    height 25%
    top 20%
    left -20%
    position absolute
    background red
    mix-blend-mode color-burn

@media screen and (min-width: 53em)
    .page--layout-1
        grid-template-columns 30% 70%
        grid-template-areas 'header header' '...  meta' 'grid grid'
    .page--layout-2
        grid-template-columns 1fr 1fr
        grid-template-areas 'header meta' '...  meta' 'grid grid'


@media screen and (max-width: 40em) 	
 .content__item--expand .content__item-imgwrap,
 .content__item--expand:nth-child(even) .content__item-imgwrap
    grid-area 1 / 1 / 3 / 3


@media screen and (max-width: 40em) 
  .content__item--expand .content__item-description,
	.content__item--expand:nth-child(even) .content__item-description,
	.content__item--wide .content__item-description,
  .content__item--wide:nth-child(even) .content__item-description
    grid-area 3 / 1 / 4 / 3
    padding 1rem 0
    width 100%
    text-align center

@media screen and (max-width: 40em)
    .content__item--wide
        .content__item-description
            padding 1rem


@media screen and (max-width: 40em) 	
  .content--alternate .content__item-title,
  .content--alternate .content__item:nth-child(even) .content__item-title
    grid-area 1 / 1 / 4 / 2
              
            
!

JS

              
                /**
* demo.js
* http://www.codrops.com
*
* Licensed under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
* 
* Copyright 2019, Codrops
* http://www.codrops.com
*/
{
    // helper functions
    const MathUtils = {
        // map number x from range [a, b] to [c, d]
        map: (x, a, b, c, d) => (x - a) * (d - c) / (b - a) + c,
        // linear interpolation
        lerp: (a, b, n) => (1 - n) * a + n * b,
        // Random float
        getRandomFloat: (min, max) => (Math.random() * (max - min) + min).toFixed(2)
    };

    // body element
    const body = document.body;
    
    // calculate the viewport size
    let winsize;
    const calcWinsize = () => winsize = {width: window.innerWidth, height: window.innerHeight};
    calcWinsize();
    // and recalculate on resize
    window.addEventListener('resize', calcWinsize);
    
    // scroll position
    let docScroll;
    // for scroll speed calculation
    let lastScroll;
    let scrollingSpeed = 0;
    // scroll position update function
    const getPageYScroll = () => docScroll = window.pageYOffset || document.documentElement.scrollTop;
    window.addEventListener('scroll', getPageYScroll);

    // Item
    class Item {
        constructor(el) {
            // the .item element
            this.DOM = {el: el};
            // the inner image
            this.DOM.image = this.DOM.el.querySelector('.content__item-img');
            this.DOM.imageWrapper = this.DOM.image.parentNode;
            this.DOM.title = this.DOM.el.querySelector('.content__item-title');
            this.renderedStyles = {
                // here we define which property will change as we scroll the page and the item is inside the viewport
                // in this case we will be:
                // - scaling the inner image
                // - translating the item's title
                // we interpolate between the previous and current value to achieve a smooth effect
                imageScale: {
                    // interpolated value
                    previous: 0, 
                    // current value
                    current: 0, 
                    // amount to interpolate
                    ease: 0.1,
                    // current value setter
                    setValue: () => {
                        const toValue = 1.5;
                        const fromValue = 1;
                        const val = MathUtils.map(this.props.top - docScroll, winsize.height, -1 * this.props.height, fromValue, toValue);
                        return Math.max(Math.min(val, toValue), fromValue);
                    }
                },
                titleTranslationY: {
                    previous: 0, 
                    current: 0, 
                    ease: 0.1,
                    fromValue: Number(MathUtils.getRandomFloat(30,100)),
                    setValue: () => {
                        const fromValue = this.renderedStyles.titleTranslationY.fromValue;
                        const toValue = -1*fromValue;
                        const val = MathUtils.map(this.props.top - docScroll, winsize.height, -1 * this.props.height, fromValue, toValue);
                        return fromValue < 0 ? Math.min(Math.max(val, fromValue), toValue) : Math.max(Math.min(val, fromValue), toValue);
                    }
                }
            };
            // gets the item's height and top (relative to the document)
            this.getSize();
            // set the initial values
            this.update();
            // use the IntersectionObserver API to check when the element is inside the viewport
            // only then the element styles will be updated
            this.observer = new IntersectionObserver((entries) => {
                entries.forEach(entry => this.isVisible = entry.intersectionRatio > 0);
            });
            this.observer.observe(this.DOM.el);
            // init/bind events
            this.initEvents();
        }
        update() {
            // sets the initial value (no interpolation)
            for (const key in this.renderedStyles ) {
                this.renderedStyles[key].current = this.renderedStyles[key].previous = this.renderedStyles[key].setValue();
            }
            // apply changes/styles
            this.layout();
        }
        getSize() {
            const rect = this.DOM.el.getBoundingClientRect();
            this.props = {
                // item's height
                height: rect.height,
                // offset top relative to the document
                top: docScroll + rect.top
            }
        }
        initEvents() {
            window.addEventListener('resize', () => this.resize());
        }
        resize() {
            // gets the item's height and top (relative to the document)
            this.getSize();
            // on resize reset sizes and update styles
            this.update();
        }
        render() {
            // update the current and interpolated values
            for (const key in this.renderedStyles ) {
                this.renderedStyles[key].current = this.renderedStyles[key].setValue();
                this.renderedStyles[key].previous = MathUtils.lerp(this.renderedStyles[key].previous, this.renderedStyles[key].current, this.renderedStyles[key].ease);
            }
            
            // and apply changes
            this.layout();
        }
        layout() {
            // scale the image
            this.DOM.image.style.transform = `scale3d(${this.renderedStyles.imageScale.previous},${this.renderedStyles.imageScale.previous},1)`;
            // translate the title
            this.DOM.title.style.transform = `translate3d(0,${this.renderedStyles.titleTranslationY.previous}px,0)`;
        }
    }

    // SmoothScroll
    class SmoothScroll {
        constructor() {
            // the <main> element
            this.DOM = {main: document.querySelector('main')};
            // the scrollable element
            // we translate this element when scrolling (y-axis)
            this.DOM.scrollable = this.DOM.main.querySelector('div[data-scroll]');
            // the items on the page
            this.items = [];
            this.DOM.content = this.DOM.main.querySelector('.content');
            [...this.DOM.content.querySelectorAll('.content__item')].forEach(item => this.items.push(new Item(item)));
            // here we define which property will change as we scroll the page
            // in this case we will be translating on the y-axis
            // we interpolate between the previous and current value to achieve the smooth scrolling effect
            this.renderedStyles = {
                translationY: {
                    // interpolated value
                    previous: 0, 
                    // current value
                    current: 0, 
                    // amount to interpolate
                    ease: 0.1,
                    // current value setter
                    // in this case the value of the translation will be the same like the document scroll
                    setValue: () => docScroll
                }
            };
            // set the body's height
            this.setSize();
            // set the initial values
            this.update();
            // the <main> element's style needs to be modified
            this.style();
            // init/bind events
            this.initEvents();
            // start the render loop
            requestAnimationFrame(() => this.render());
        }
        update() {
            // sets the initial value (no interpolation) - translate the scroll value
            for (const key in this.renderedStyles ) {
                this.renderedStyles[key].current = this.renderedStyles[key].previous = this.renderedStyles[key].setValue();   
            }   
            // translate the scrollable element
            this.layout();
        }
        layout() {
            this.DOM.scrollable.style.transform = `translate3d(0,${-1*this.renderedStyles.translationY.previous}px,0)`;
        }
        setSize() {
            // set the heigh of the body in order to keep the scrollbar on the page
            body.style.height = `${this.DOM.scrollable.scrollHeight}px`;
        }
        style() {
            // the <main> needs to "stick" to the screen and not scroll
            // for that we set it to position fixed and overflow hidden 
            this.DOM.main.style.position = 'fixed';
            this.DOM.main.style.width = this.DOM.main.style.height = '100%';
            this.DOM.main.style.top = this.DOM.main.style.left = 0;
            this.DOM.main.style.overflow = 'hidden';
        }
        initEvents() {
            // on resize reset the body's height
            window.addEventListener('resize', () => this.setSize());
        }
        render() {
            // Get scrolling speed
            // Update lastScroll
            scrollingSpeed = Math.abs(docScroll - lastScroll);
            lastScroll = docScroll;
            
            // update the current and interpolated values
            for (const key in this.renderedStyles ) {
                this.renderedStyles[key].current = this.renderedStyles[key].setValue();
                this.renderedStyles[key].previous = MathUtils.lerp(this.renderedStyles[key].previous, this.renderedStyles[key].current, this.renderedStyles[key].ease);    
            }
            // and translate the scrollable element
            this.layout();
            
            // for every item
            for (const item of this.items) {
                // if the item is inside the viewport call it's render function
                // this will update item's styles, based on the document scroll value and the item's position on the viewport
                if ( item.isVisible ) {
                    if ( item.insideViewport ) {
                        item.render();
                    }
                    else {
                        item.insideViewport = true;
                        item.update();
                    }
                }
                else {
                    item.insideViewport = false;
                }
            }
            
            // loop..
            requestAnimationFrame(() => this.render());
        }
    }

    /***********************************/
    /********** Preload stuff **********/

    // Preload images
    const preloadImages = () => {
        return new Promise((resolve, reject) => {
            imagesLoaded(document.querySelectorAll('.content__item-img'), {background: true}, resolve);
        });
    };
    
    // And then..
    preloadImages().then(() => {
        // Remove the loader
        document.body.classList.remove('loading');
        // Get the scroll position and update the lastScroll variable
        getPageYScroll();
        lastScroll = docScroll;
        // Initialize the Smooth Scrolling
        new SmoothScroll();
    });
}
              
            
!
999px

Console