Skip to content
Snippets Groups Projects
_utils.sass 13.44 KiB
@function pxToRem($size)
    $remSize: $size / 16
    @return #{$remSize}rem

@function px2rem($size)
    @return pxToRem($size)

$space-unit: 4 !default
@function space($spaces: 1)
    @return #{$spaces * $space-unit / 16}rem

@mixin in-page-with-sidebar
    @include media-breakpoint-up(desktop)
        body:not(.full-width) &
            @content

@mixin in-page-without-sidebar
    @include media-breakpoint-up(desktop)
        main > .blocks &,
        body.full-width &,
            @content

// Use this mixin to override with-aside or without-aside rules
@mixin in-page-with-or-without-sidebar
    @include media-breakpoint-up(desktop)
        body:not(.full-width) &,
        main > .blocks &,
        body.full-width &,
            @content

// Aliases
@mixin full-page
    @include in-page-without-sidebar
        @content

@mixin not-full-page
    @include in-page-with-sidebar
        @content

@mixin in-page-program
    .programs__section &
        @content

@function half($size)
    @return calc(#{$size} / 2)

@mixin link($color: $link-color, $unhover_decorated: true)
    color: $color
    text-decoration-line: underline
    text-decoration-thickness: $link-underline-thickness
    text-underline-offset: $link-underline-offset
    transition: $link-transition
    @if $unhover_decorated
        text-decoration-color: rgba($color, $link-unhover-decoration-color-alpha)
    @else 
        text-decoration-color: transparent
    &:hover
        text-decoration-color: rgba($color, 1)
        text-decoration-thickness: 1px

@mixin link-hovered-underline-only
    &:not(:hover)
        text-decoration: transparent

@mixin hover-translate-icon($pseudo: after, $distance: 10)
    &::#{$pseudo}
        display: inline-block
        transition: transform 0.55s $arrow-ease-transition
        transform: translateX(0)
    &:hover
        &::#{$pseudo}
            transform: translateX(#{px2rem($distance)})

@mixin sticky($offset-y: 0)
    position: sticky
    top: $offset-y
    @if $header-sticky-enabled
        transition: top $header-sticky-transition
        html:not(.is-scrolling-down) &
            top: calc(var(--header-height) + #{$offset-y})

// NEW UTILS
@mixin icon($icon-name: '', $pseudo-element: before, $non-breaking: false)
    &::#{$pseudo-element}
        content: map-get($icons, $icon-name)
        display: inline-block
        font-family: 'Icon'
        font-style: normal
        font-variant: normal
        font-weight: normal
        line-height: 1
        speak: never
        text-transform: none
        vertical-align: middle
        @if $non-breaking
            content: " #{map-get($icons, $icon-name)}"
            display: inline
        @content // TODO : important de documenter ça

@mixin icon-block($icon-name: '', $pseudo-element: before, $non-breaking: false)
    @include icon($icon-name, $pseudo-element, $non-breaking)
        font-size: 44px
        display: inline
        @content

@mixin container
    margin-left: auto
    margin-right: auto
    max-width: $grid-max-width
    padding-left: half($grid-gutter-sm)
    padding-right: half($grid-gutter-sm)
    width: 100%
    @include media-breakpoint-up(desktop)
        padding-left: $grid-gutter
        padding-right: $grid-gutter

@mixin grid($cols: 12, $breakpoint: false, $gap-y: $grid-gutter, $gap-x: $grid-gutter)
    word-break: break-word
    @if $breakpoint
        @include media-breakpoint-up($breakpoint)
            display: grid
            grid-gap: $gap-y $gap-x
            grid-template-columns: repeat($cols, 1fr)
    @else
        display: grid
        grid-gap: $gap-y $gap-x
        grid-template-columns: repeat($cols, 1fr)
    @include media-breakpoint-down(desktop)
        grid-gap: $grid-gutter-sm

// This must be used for content inside columns
@function col($quantity, $base: 12)
    $quantity-on-base: $quantity / $base * 12
    $width: calc( (100% + #{$grid-gutter}) / 12 * #{$quantity-on-base} - #{$grid-gutter} )
    @return #{$width}

// This must be used for offset, outside columns
@function offset($quantity, $base: 12)
    $quantity-on-base: $quantity / $base * 12
    $width: calc( (100% + #{$grid-gutter}) / 12 * #{$quantity-on-base} )
    @return #{$width}

// This must be used for offset, inside columns
@function col-offset($quantity, $base: 12)
    $quantity-on-base: $quantity / $base * 12
    $width: calc( (100% + #{$grid-gutter}) / 12 * #{$quantity-on-base} + #{$grid-gutter})
    @return #{$width}

@function col-outside-container($quantity, $base: 12)
    $responsive-grid-width: Min(100vw, (#{$grid-max-width}))
    @return calc((#{$responsive-grid-width} + #{$grid-gutter} * 2) / #{$base} * #{$quantity} - #{$grid-gutter} * 2)

@function container-margin-x
    @return Max(#{$grid-gutter}, calc(50vw - #{$grid-max-width} / 2 + #{$grid-gutter}))

@mixin container-margin-left
    margin-left: container-margin-x()

@mixin container-margin-right
    margin-right: container-margin-x()

@mixin stretched-link($pseudo-element: after)
    &::#{$pseudo-element}
        bottom: 0
        content: ''
        left: 0
        position: absolute
        right: 0
        top: 0
        z-index: $zindex-stretched-link

@mixin aspect-ratio($ratio, $selector: 'iframe', $background: false)
    @if $background
        aspect-ratio: #{$ratio}
        background: $background
    #{$selector}
        aspect-ratio: #{$ratio}
        display: block
        width: 100%

@mixin handle-svg-fit
    picture.is-svg
        img
            object-fit: contain

@mixin article($background: null)
    position: relative
    display: flex
    flex-direction: column
    .media
        @include handle-svg-fit
        margin-bottom: $spacing1
        order: -1
        overflow: hidden
        img
            display: block
            object-fit: cover
            @if $article-media-aspect-ratio
                aspect-ratio: $article-media-aspect-ratio
    h2, h3, [itemprop=headline]
        @include h3
        a
            display: block
            @include stretched-link
            text-decoration: none
    p + time
            margin-top: $spacing0
    time
        @include meta
        color: $color-text-alt
        display: block

@mixin post-time-author-flex
    .post-meta
        display: flex
        flex-wrap: wrap
    .post-author p::before
        content: ' • '


@mixin list-section
    @include list-reset
    > li
        border-bottom: 1px solid $color-border
        padding-bottom: $spacing1
        padding-top: $spacing1
        position: relative
        > .title
            @include h2
            transition: color 0.55s
            @include media-breakpoint-down(desktop)
                @include icon(arrow-right, after, true)
                    bottom: $spacing0
                    position: absolute
                    right: 0
            @include media-breakpoint-up(desktop)
                @include arrow-right-hover
                display: block
                &::after
                    transform: translateX(0)
                    position: relative
                &:hover
                    &::after
                        transform: translateX($spacing0)
        a
            text-decoration: none
            &:hover
                color: $color-accent

        @include media-breakpoint-down(desktop)
            a:nth-child(2)
                margin-top: calc(#{$spacing0} / 2)
            a, p
                display: block
        @include media-breakpoint-up(desktop)
            align-items: baseline
            display: flex
            justify-content: space-between
            

@mixin visually-hidden
    clip: rect(0,0,0,0) !important
    border: 0 !important
    height: 1px !important
    margin: -1px !important
    overflow: hidden !important
    padding: 0 !important
    position: absolute !important
    white-space: nowrap !important
    width: 1px !important

@mixin button-reset
    appearance: none
    background: transparent
    border: none
    border-radius: 0
    cursor: pointer
    user-select: none
    &:active,
    &:focus
        box-shadow: inset 0 0 0 0.25rem rgba($color-text, 0.25)
        // TODO : vérifier si l'outline 0 est vraiment nécessaire
        // outline: 0

@mixin list-reset
    list-style: none
    padding-left: 0
    margin-bottom: 0
    margin-top: 0

@mixin inset($top: 0, $right: $top, $bottom: $top, $left: $top)
    inset: $top $right $bottom $left
    // polyfill for inset
    @supports not (inset: $top)
        bottom: $bottom
        left: $left
        right: $right
        top: $top

@function color-contrast($color, $amount)
    @if lightness($color) > 50%
        $amount: $amount * -1
    @return scale-color($color, $lightness: $amount)

@mixin button-icon($icon: false)
    @include button-reset
    line-height: $body-line-height
    border: 1px solid $hero-color
    padding: half($spacing0) $spacing1
    white-space: nowrap
    @if $icon
        @include icon-block($icon, after)

@mixin text-underline
    text-decoration-color: $color-border
    text-decoration-line: underline
    text-decoration-thickness: 1px
    text-underline-offset: 3px
    text-decoration-line: underline

@mixin arrow-right-hover
    position: relative
    display: flex
    justify-content: space-between
    align-items: center
    @include icon(arrow, after)
        opacity: 0
        transform: translateX(-20px)
        transition: 0.55s $arrow-ease-transition
        position: absolute
        right: 0
    &:hover
        &:after
            opacity: 1
            transform: translateX(0)

@mixin top-flex
    @include in-page-without-sidebar
        align-items: baseline
        display: flex
        .block-title
            width: col(4)
            &:not(.hidden) + .description
                margin-left: $grid-gutter
        .description
            margin-top: 0
            width: col(8)

@mixin collapsed-figcaption
    figcaption
        @include meta
        color: $color-text-alt
        position: absolute
        display: block
        left: 0
        right: 0
        text-align: right
        z-index: 10
        &::before
            @include meta
            content: '©'
            position: absolute
            right: 0
            top: 0
            background: $hero-background-color
            text-align: center
            padding: half($spacing0)
            display: block
        .credit-content
            @include meta
            background: $hero-background-color
            display: none
            padding: half($spacing0)
            padding-right: $spacing1
        a
            color: inherit
            text-decoration-color: inherit
        &:focus
            .credit-content
                display: block
        @include media-breakpoint-up(desktop)
            &:before
                padding-right: 0
        @include media-breakpoint-down(desktop)
            position: relative
            background: $color-background
            &::before
                background: transparent
            .credit-content
                display: block
                background: transparent
                position: relative

    &:hover
        figcaption .credit-content
            display: block

@mixin dropdown-i18n
    position: relative
    .dropdown-menu
        margin-top: $header-nav-padding-y
        padding-left: $spacing1
        padding-bottom: $spacing0
        padding-top: $spacing0
        padding-right: $spacing1
        right: 0
    li a
        padding-bottom: half($spacing0)
        padding-top: half($spacing0)
        display: block
        &.is-checked
            display: flex
            justify-content: space-between
            align-items: center
            gap: $spacing2
            text-decoration: none !important
            @include icon(caret, after)
                transform: rotate(-14deg) skewX(-30deg)

// https://gist.github.com/jonathantneal/d0460e5c2d5d7f9bc5e6
@function str-replace($string, $search, $replace: "")
	$index: str-index($string, $search)
	@if $index
		@return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
	@return $string
    
@mixin font-face($name, $path, $weight: 400, $style: normal, $exts: (eot woff2 woff ttf svg))
  $src: null

  $extmods: (eot:"?", svg:"#" + str-replace($name," ","_"))
  $formats: (otf: "opentype", ttf: "truetype")

  @each $ext in $exts
    $extmod: if(map-has-key($extmods, $ext), $ext + map-get($extmods, $ext), $ext)
    $format: if(map-has-key($formats, $ext), map-get($formats, $ext), $ext)
    $src: append($src, url(quote("/assets/fonts/" + $path + "." + $extmod)) format(quote($format)), comma,)

  @font-face
    font-family: quote($name)
    font-style: $style
    font-weight: $weight
    font-display: swap
    src: $src

@mixin sidebar($side: start)
    @include media-breakpoint-down(desktop)
        padding: 0 half($grid-gutter-sm)
        margin-bottom: $spacing3
    @include media-breakpoint-up(desktop)
        @if $side == start
            @include container-margin-left
            left: 0
        @else
            @include container-margin-right
            right: 0
        margin-top: 0
        top: 0
        height: 100%
        position: absolute
        width: col-outside-container(4)
        & > div
            @include sticky($spacing1)
        .toc-container
            border-top: 1px solid $color-border
            padding-top: $spacing1
            position: static
            margin-left: 0

// Old browsers support

@mixin browser-under-safari-16
    @media not all and (min-resolution:.001dpcm) 
            @supports (-webkit-appearance:none) and (display:flow-root)
                @content