# Styles
This directory contains the browser reset and base styling for the whole platform, Sass functions and mixins to handle repeated code, and reusable variables for colours and common spacing.

We use [Sass](https://sass-lang.com/) for our styling, which gives us the flexibility of using vanilla CSS's powerful features like [layers](#layer) and container queries, while also being able to write less code with variables, mixins, and functions.

- [Set-up](#set-up)
- [Cascade layers (`@layer`)](#layer)
- [Browser reset](#browser-reset)
- [Base styling](#base-styling)
  - [Accessibility](#accessibility)
  - [Buttons](#buttons)
  - [Text](#text)
  - [Titles](#titles)
- [Functions and mixins](#functions-and-mixins)
  - [`get-map`](#get-map)
  - [`px-to-rem`](#px-to-rem)
  - [`image`](#image)
  - [`breakpoint`](#breakpoint)
  - [`animate` and `transition`](#animate-and-transition)
  - [`container-query`](#container-query)
  - [`amend-selector-path`](#amend-selector-path)
  - [Repeated styles](#repeated-styles)
  - [Lesser needed mixins](#lesser-needed-mixins)
    - [`query-touchscreen` and `query-not-touchscreen`](#query-touchscreen-and-query-not-touchscreen)
    - [`target-safari`](#target-safari)
    - [`hidden-element` and `show-hidden-element`](#hidden-element-and-show-hidden-element)
- [Variables](#variables)
  - [Colours](#colours)


---

## Set-up
For accessibility, we code all of our sizes in `rem`s. It's important that _every_ size is in `rem`s, otherwise some elements will resize when the user changes their browser's font size, but others won't, leading to a broken layout.

In Figma, all of the sizes are in pixels. To make it easier for us to work with (and reason about), we add them as variables in `px` and then convert them to `rem`s using a [custom mixin, `px-to-rem`](#px-to-rem).

We place sizes in Sass maps at the top of the file and use a [custom mixin, `get-map`](#get-map), to pull the value. This makes it extremely fast for us to change sizes, as we only need to update the value in one place.

See [functions and mixins](#functions-and-mixins) for more information on how these work.

<br />

---

<br />

## Cascade layers (`@layer`)

We're using [CSS cascade layers (`@layer`)](https://www.smashingmagazine.com/2022/01/introduction-css-cascade-layers/) to organise our styles, to help us avoid specificity issues and to make it easier to find and update styles.

The `./index.scss` file defines the order of our layers, with the splash styling (the bouncing Apolitical logo we use as a loader) as the lowest.

If you need to overwrite CSS, you don't need to create an overly specific (and less performant) rule or add an `!important` (as an aside: please, please [never add an `!important`](https://dev.to/alvaromontoro/using-important-in-css-75c)) - just put the styling in a higher layer.

The only 'gotcha' with this is importing external CSS; any styling that _isn't_ in a CSS layer is considered more important than styling in CSS layers, so styling from an external package will overwrite our styling. To change this, we need to write any styling that needs to overwrite external styling outside of CSS layers. (See the `InviteForm` component for an example.)

<br />

---

<br />

## Browser reset

Every browser has its own styling for elements; to give us full control over the design of the page, we need to remove the default styling before we apply our own.

`@layer reset` is the second layer of our CSS cascade, so it's the first thing applied to the page after the logo loader (`splash`). It's a reset, not a normal CSS file, so it doesn't have any classes or IDs - it just sets the default styling for all elements.

We use a version of [Eric Meyer's browser reset](https://meyerweb.com/eric/tools/css/reset/), though we don't put any kind of `font` overrides in each element; we found this could cause inconsistencies when the base styling was being pulled from `component-library`, as it would override the Next page styling.

The styling lives in `/reset`.

<br />

---

<br />

## Base styling

We use base styling to ensure common elements on every page - things like text, headings, buttons, links, and forms - are styled in a single place and don't need to be redefined for each page. When we add a `p` or `button` to the site, it's already styled.

It's the third layer in our CSS cascade (`@layer base`), so it's applied after the browser reset, but can be overwritten on a component level if an element inside a particular component needs to look different.

Most of our base styling is for elements which we don't have components for - like `p` and `h1` - but we also keep all of the `button` and `form` styling here. While there is a `Button` component, the same styles are often used on elements which aren't interactive, like the connection statuses on profiles, and we have some form components, like `RichTextEditor` and `Search` which don't use the `Form` component; we've pulled the styling out to our base styling, as these are all important building blocks of our UI and aren't strictly linked to their functionality.


### Accessibility
#### Highlighting focus
Highlighting where the focus is, especially for interactive elements like links, buttons, and inputs, is an important part of the [WCAG AA guidelines](https://www.w3.org/WAI/WCAG21/Understanding/focus-visible.html).

In our base styling, we remove the browser's default `outline` on focused elements and add a `box-shadow`, which gives us a bit more control over the look.

Please think twice before removing the `box-shadow` on a focused element as the page its on will immediately fail accessibility.

#### Hiding text visually
We sometimes want screen readers to receive text that we don't want on the page. While it sometimes makes sense to use an `aria-label` on an element, that will override the existing text. When we want to add text, rather than replacing it, we have the `.hidden` class. It uses the `hidden-element` mixin, which can be called directly if you need more control. It sets the width and height of an element to `0`, rendering it invisible visually but ensuring it's still read out to screen readers. (Using something like `display: none` would also hide the content from screen readers.)

It also has an optional class, `.show-on-focus`, which makes the element show visually if it is navigated to by keyboard. For example, we have a link above our iframes which allows people to skip past the iframe, in case the third party content is not accessible. We don't want to show this text all the time, but we do want to show it to people navigating by keyboard, so they don't click something they don't mean to.

The class uses the mixin `show-hidden-element`, which can also be used independently.

The component `VisuallyHidden` uses this styling behind-the-scenes; it's best to use that directly if you're doing something simple, but the classes and mixins are available if you need them.


### Buttons
The `Button` component in the component library renders most of the buttons on the site. It can render `button` or `a` components, depending on if we need something to happen on click or if we just want a styled link. But we also sometimes want styling on elements which aren't interactive, like the connection statuses on profiles.

See the component library for in-depth documentation on `Button`.

#### Variations

By default, buttons have `primary` styling (a purple background and white text) and don't need a `primary` class to be styled. Buttons can also be `secondary` (a ghost button with a purple border) or `tertiary` (no border or background color). There are additional variations for `destructive` (only available on primary buttons), `muted` (only available on secondary and tertiary buttons), and `disabled`.

We also have a temporary class, `new`, which renders black buttons; this is used for the homepage, which is in the new design. We should ideally update the styling of `primary`, as this will affect the whole site in a single line of code, rather than needing manual button-by-button work, but we currently need more control over each button.

#### Sizes

By default, buttons are `medium`. They don't need a class to be styled. Buttons can also be `small` or `large`.

Buttons rendered with `Button` can change their size depending on the width of the viewport. See the component library documentation for an example.

#### Icons

Buttons can have an icon but - because we can't mix pre-processing and data passed into CSS for file names, like CSS variables or data attributes - icons on buttons need to be set up in advance with classes. The list of available icons is maintained in `./base/buttons/_icons.scss`. To add a new icon, add a new item to the list with arguments for the sizes on large, medium, and small buttons.

To add an icon to a button, add the class name `icon [file name of icon without file extension]`, e.g. `icon heart_empty`.

The icon file, with a matching name, should be uploaded to the `apolitical-assets` bucket on Google Cloud Platform. (Referred to as `$assets-url` in the styling.)

You can also pass `left` or `right` to position the icon - this will add margin between the text and the icon. e.g. `icon heart_empty right`.

You can change the icon on hover by adding a second file name, prepended with `hover_`, e.g. `icon heart_empty hover_heart` will change the heart icon from an outline to a filled in heart on hover.

See the `base/buttons/_icons.scss` file for the full list of available icons, and details on how to handle different sizes.

### Text
The text used across the site for paragraphs and lists, is styled in `base/_text.scss`.

We have three sizes of text used across the site: large (which is our default size, defined in `$default`), medium, and small.

These sizes also have different `font-size`s and `line-height`s, depending on the user's viewport.

The large size is applied by default. It can be changed to medium or small using a class, `.text-medium` or `.text-small`.

For a lot of text, the class should go on the parent element, e.g. on a `ul` or an `article`. If a single `p` is a different size, it can have the class directly.

In `base/_text.scss`, this is handled with the `$standard-text-elements` variable, which lists all of the elements which can be styled with the class on the parent and directly. (We also have some special styling, just for `p`s, to handle margins which we don't want on every text element.)

`$special-text-elements` is a list of elements which can only take the class directly, such as `a` and `span`; this is because there will often be a lot of them in a component and we don't want them to deviate from their given styles unless they have an explicit class.

(We list the elements in variables as they're used in multiple media queries and we don't want to accidentally miss anything.)

### Titles
The titles used across the site for headings and subheadings are styled in `base/_titles.scss`.

Our Figma files have six heading styles, named h1-h6, as well as subheading and pre-title styles. 

By default, we code `h1`s to match the 'h1' styling in Figma, `h2`s to match the 'h2' styling, and so on. However, sometimes, for aesthetic reasons, we may want the styling of the 'h4' heading, but for accessibility and semantic mark-up, the heading would need to be an `h2` or a `p`. 

All of the styling for each heading lives in a mixin, which can be included on any element, so we can use the styles where we need them.

<br />

---

<br />

## Functions and mixins
We have a lot of custom functions and mixins to make our code more maintainable and easier to write.

You won't likely need to change these functions but you should be using them in your code, to make it faster, more accessible, and more maintainable.

### `get-map`
Sass has a built-in function to get a value from a map (`map.get`), but it silently fails when the value can't be reached, which means a small typo in a key can mean styling you're expecting is never applied. It's easy to miss and can make us lose time.

Instead, we use a custom function, `get-map`, which throws an error if the key can't be found, so we're aware of any issues and can quickly fix them.

You shouldn't need to amend the function - just call it wherever you're retrieving a value from a map. It can take two levels of map, e.g.:

```
  $map: (
    value: 1,
    key: (
      subkey: 2
    )
  );
  
  get-map($map, 'value') // 1
  get-map($map, 'key', 'subkey') // 2
```

### `px-to-rem`
It's important for accessibility that we use `rem`s, which can be scaled for the user's needs (e.g. someone with poor eyesight might need to have the screen zoomed in). Every unit of measurement should be a `rem`, as mixing different units can make the site look broken.

`1rem` will always be relative for the user, but `100px` will always be `100px`. If the user resizes their browser, `font-size: 1rem` text inside a `height: 100px` box will break out of the layout. A `6.25rem` box will resize with the text and always contain it.

However, `6.25rem` is a lot harder for us to reason about than `100px`; we know how big `100px` is and we know if it matches the Figma designs, which are always presented in `px`. 

To make it easier for us, we use the same units as Figma and use `px-to-rem` to convert them.

`px-to-rem` needs a `$size` value. We use [`get-map`](#get-map) to pull the value from a map, so we only need to update the value in one place, and pass it in, like:

```
$wrapper: ('height': 100);

#wrapper {
  height: px-to-rem(get-map($wrapper, 'height'));
}
```

By default, `px-to-rem` uses `16px` as its baseline - `1rem` is `16px`. If you need to change this, you can pass a second argument, e.g. `px-to-rem(100, 10)` will convert `100px` to `10rem`, but this isn't likely to come up!


### `animate` and `transition`
Anywhere we use CSS animations or transitions on our platform, we need to use the `animate` and `transition` mixins.

Some users have conditions like epilepsy which can be triggered by animations; for accessibility, we need to avoid doing any kind of animation if the user has set their device to reduce motion. Devices won't just do this automatically - we are responsible for not triggering animations if the user has this setting.

There is a special [media query, `prefers-reduced-motion`](https://www.smashingmagazine.com/2021/10/respecting-users-motion-preferences/) which we can use to target the users who have not opted out of animations; to avoid having to set this up everywhere you need it, and to avoid accidentally missing it and making your component inaccessible, the `animate` and `transition` mixins will do all the work for you.

#### `animate`
`animate` is a very simple mixin which just wraps the code inside a media query. It renders the new styling [`@at-root`](https://sass-lang.com/documentation/at-rules/at-root/), in the root of the document, so you don't have to set up the animation out of context. 

To use CSS animations, wrap your code in the `animate` mixin, like:

```
.element {
  transform: translateX(0%); 
  
  @include animate {
    @keyframes loading {
      100% {
        transform: translateX(100%);
      }
    }
  
    // & is the selector to this point - `.element`
    // We're just making sure we're only adding the animation when the user has not
    // asked for reduced motion
    & {
      animation: loading 0.8s infinite;
    }
  }
}
```

You can see the `animate` mixin in action in the `LoadingPlaceholder` component.

#### `transition`
`transition` is a more complex mixin to read, though it's unlikely you will need to make changes to it.

It accepts a `$proprerties` argument - the CSS properties you want to transition, like `background` or `color` - and three optional arguments, the duration of the transition, what function to use for the animation, and the delay.

For performance reasons, it's important to only pass the properties you need to transition, rather than using `all`. Otherwise, everything will be listened for, which will slow down the page.

You can listen for multiple properties on a single element, and each property can have its own duration, function, and delay. They can also use the same, if you'd prefer.

This will transition changes to the `color` and `text-decoration-color`, using the default duration, function, and delay:
```
a {
  @include transition(color text-decoration-color);
}  
```

This will transition the `color` over `0.2s` with no delay and the `text-decoration-color` over `0.5s` with a delay of `0.3s`. They will both use the `ease-in-out` function:
```
a {
  @include transition(color text-decoration-color, 0.2s 0.5s, ease-in-out, 0s 0.3s);
}  
```

The mixin takes all the arguments, breaks them by space, and turns it into the correct transition CSS, and ensures users who have asked for reduced motion don't see it.

### `image`
We use the `image` mixin to handle background images on the site--this lets us easily handle CSS masks without repeating a lot of code, and it lets us have a single reference to our assets bucket, so we can easily change things.

The mixin has one required prop, which is the image file (everything after the asset bucket address, which is defined in variables), and optional props for if it's a CSS mask, the size, repeat, and position properties.

Passing just the image, like:
```
@include image('icons/tick.svg');
```
would render the image as a background image. It couldn't be recoloured.

Passing `true` as the second argument, like:
```
@include image('icons/tick.svg', true);
```
would render a CSS mask, so the icon could be recoloured to anything using `background`. This is especially useful for hover and disabled states, where we want to change the color without downloading a new asset.


### `breakpoint`
We have a custom mixin to handle media queries for us, which allows us to use easy shortcuts for the most common viewport sizes. When the design changes, we can remap these names to the new sizes, rather than having to update every media query in the code.

The common sizes are defined in `$breakpoint-sizes`. We only name the most common sizes, to keep the map manageable. It's possible to pass a number to the `breakpoint` query instead, to target a viewport we only care about in one-off cases. It's passed in as the second argument.

Using the mixin also lets us write less code for more complex media queries.

The mixin takes two arguments: the media feature you are targeting, like `max-height` or `min-width` and the viewport size, either as a named variable, like `md` or as a number, like `951`, which will be converted to `px`.

This will apply the styles when the viewport width is at least the `md` value, `769px`:
```
@include breakpoint(min-width, md) {
  // styles
}
```

You can pass multiple media features and viewport sizes for media queries which have a minimum and maximum viewport size, like:
```
@include breakpoint(min-width max-width, md lg) {
  // styles
}
```
The styles in this query will apply when the viewport width is at least `769px` and less than `1180px`.


### `container-query`
[Container queries](https://www.smashingmagazine.com/2021/05/complete-guide-css-container-queries/) are very similar to media queries but apply to an element's parent. We may sometimes want to change the style of an element when it's in a smaller area, like a sidebar, which won't always correlate to the viewport being small.

For example, on individual article pages, we show a `MarketingBanner` with smaller padding and no purple circle element in the sidebar and in its normal styling after the article--using its parent lets us change things in specific circumstances without needing to add a lot of extra classes.

The `container-query` mixin handles this for us. It uses the same named breakpoint sizes as `breakpoint`, so we can easily change the sizes in one place.

Instead of a media feature, its first argument is the `container-name` of the container parent, which is set as part of the parent's normal styling alongside the `container-type`. (NB: this is needed for container queries to behave as expected. Be aware container queries don't play nicely with flex styling.)


### `amend-selector-path`
Sass has a built-in function,`@at-root`, which allows you to write styling in context which is rendered in the root of the final CSS file, instead of in the current selector--like a Sass version of `React.Portal`.

We can also use it to overwrite the current selector path for a block of styling, which is useful if we have styling which should only be applied when there's a certain parent element in the path. `amend-selector-path` is a mixin which does this for us, to make it more obvious what the code is doing and to make it easier to read.

It takes two arguments, the old path and the new one.

All styles inside this would only apply to the link inside the sidebar:
```
@include amend-selector-path('a', '.sidebar a') {
  // styles
}
```


### Repeated styles
We have a lot of repeated styles across the site, which are used across different elements. To avoid duplicating code, we have pulled the common ones out into mixins.

- `dashed-border` adds a dashed border with an inline SVG.
- `line-under-text` adds a colored line beneath a title. It accepts arguments for the colour and size.
- `full-width-section` makes an element the full width of the user's viewport. This is used in the `FullWidthSection` component (which is the recommended component to use, over this mixin), but the styling is available as a mixin in cases where it might not make sense to render a component wrapper.

### Lesser needed mixins
There are some mixins that aren't used as often, which are worth knowing about.

#### `query-touchscreen` and `query-not-touchscreen`
If you want to apply styling to only touchscreen (or non-touchscreen) devices--for example, applying hover states, you can use these mizins to limit what receives the styling.

#### `target-safari`
Safari has some unique bugs which can make it difficult to work with. We have a mixin, `target-safari`, which can be used to target Safari specifically, to fix these bugs.


#### `hidden-element` and `show-hidden-element`
We sometimes want to add text to the page which we don't want to be visible, but we do want to be read out by screen readers. This is often used for accessibility, to give more context to a user who can't see the screen.

We have classes and a component, `VisuallyHidden` (see the ['hiding text visually'](#hiding-text-visually)) section, which utilise these mixins. If you need to, you can call them directly.

<br />

---

<br />

## Variables
The variables that are re-used across the site are defined in `./variables`, such as asset bucket URLs, common sizes, and colours.

### Sizes
We don't store variables for repeated margins/paddings, like `md-padding`, as sizes are unlikely to stay the same between redesigns. Instead, we put those in maps at the top of every component styling file.

However, there are a few widths we frequently use for the width of the content of a page which are likely to change together when the site is redesigned. We store these in variables in `./variables/sizes/_common`, so we can manage them from one place.

There are also some variables whose sizes we need to be able to access across different components. For example, as our header can animate in and out of the layout, we need to know what height it is in many places, to ensure we're never covering content when it shows. We store the maps the header uses for its sizes in `./variables/sizes/_header`, so we can import them anywhere they're needed.

### Colours
Our colour variables are defined in `./variables/colors`.

We have two separate files:

- `colors.scss` defines the colours themselves, using the names as they're defined in Figma, like `b700`. **We never call these values directly in the code.**

Because the colours are described as colours (`b700`), rather than by what they're used for (`link_hover`), we can't rely on these names. In a future redesign, the links might become pale pink - at which point `b700` would no longer make sense, and would make the code hard to read and reason about. We also can't guarantee that everything which used to be `b700` is now pink in the redesign - some things may become yellow or green instead.

We can't rely on these colours making sense in the future, so we don't use them directly in the code.

- `./theme/index.scss` defines a `$theme` map which describes where the colours are used, rather than what the colours look like, e.g. `card_bg` for the default background colour of cards and `card_hover_bg` for the background colour cards should change to on hover.

Using these names, rather than the direct colour values, future-proofs the code against redesigns and makes it easy to maintain; when we redesign the platform, we can update the colours in one place, rather than having to amend every component.

To avoid `theme` being a giant, difficult-to-navigate file, we split out all the values by the component directory they're in (e.g. `base`, `buttons`, `cards`, etc.) and then import them into `./theme/index.scss`. We use a Sass function to pull everything into the `$theme` variable, so we don't need to know exactly what's in each file.

