# Breakpoint Systems

When designing for the web, it is important to keep in mind what different users will reach you in different devices with different screen sizes and orientations.

In the early days of web design, pages were built to target a particular screen size.

If the user had a larger or smaller screen than the designer expected, results ranged from unwanted scroll bars to overly long line lengths, and poor use of space.

As more diverse screen sizes became available, the concept of responsive web design (RWD) appeared, a set of practices that allows web pages to alter their layout and appearance to suit different screen widths, resolutions, etc.

## Responsive design

The term responsive design was coined by Ethan Marcotte in 2010 and described the use of multiple techniques:

- Fluid grids, something which was already being explored by Gillenwater, and can be read up on in Marcotte’s article, Fluid Grids (published in 2009 on A List Apart).

- Fluid images. Using a very simple technique of setting the max-width property to 100%, images would scale down smaller if their containing column became narrower than the image’s intrinsic size, but never grow larger. This enables an image to scale down to fit in a flexibly-sized column, rather than overflow it.

- The third key component was the media query. Media Queries enable the type of layout switch that Cameron Adams had previously explored using JavaScript, using only CSS. Rather than having one layout for all screen sizes, the layout could be changed. Sidebars could be repositioned for the smaller screen, or alternate navigation could be displayed.

## Media Queries

Responsive design was only able to emerge due to the media query. The Media Queries Level 3 specification became a Candidate Recommendation in 2009, meaning that it was deemed ready for implementation in browsers.

Media Queries allow us to run a series of tests (e.g. whether the user’s screen is greater than a certain width, or a certain resolution) and apply CSS selectively to style the page appropriately for the user’s needs.

## Breakpoints

By defining a set “points” where these Media Queries will apply its different rules, we are effectively creating breakpoints where the styling and layout of the page changes. Many frontend frameworks reuse a set of tested and tried breakpoints, making that set of breakpoints its `breakpoint system`.

Example bootstrap 5 breakpoints: - xs: Screen width from 0 to 576px - sm: Screen width above 576px - md: Screen width above 768px - lg: Screen width above 992px - xl: Screen width above 1200px - xxl: Screen width above 1400px

Keep in mind that users expect any website to be perfectly complementary with every single device they own – desktop, tablet, or mobile. If a website’s responsive design does not align with a certain device resolution (especially a commonly used device), the site is at risk of missing out on a segment of its target audience. Avoid this by investing time and research into defining breakpoints at the beginning of a project.

The amount of effort that goes into defining responsive breakpoints is directly proportional to the experience of the end-user.

## Breakpoints in Shiny

If you are familiar with base shiny or other css based frameworks, you might have even used these systems without realizing;

For example, using the `fluidRow()` function will trigger layout changes to your `columns()` at specific screen sizes, based on bootstrap 3 breakpoints (The base CSS framework in shiny).

While its great to have this done automatically, it also comes with many constrains and does not allow for fine control of these layout changes.

Very often for more complex layouts, you may often find yourself writing additional CSS to add new behavior for specific elements or screen sizes.

## Breakpoints with imola

Imola takes a slightly different approach to breakpoints:

- Out of the box it uses the same breakpoint system as base shiny (bootstrap 3).
- You can change the default breakpoint system at the application level, but also at a component level.
- For each `grid` and `flex` function named attribute you are able to pass either a `value` for that attribute or a `named list of different values` for different breakpoints.

Names that can be used in function attributes depend on what breakpoint names are available, we can use `getBreakpointSystem()` to see the active breakpoint system:

    Imola Breakpoint System
    Name:  bootstrap3
    description:  No description

    Available Breakpoints (name)   Minimum screen size (px)   Maximum screen size (px)
    -----------------------------  -------------------------  -------------------------
    xs                             NULL                       575
    sm                             NULL                       767
    md                             NULL                       991
    lg                             NULL                       1199
    xl                             1200                       NULL
    -----------------------------

Using `getBreakpointSystem()` with a name also allows us to get a specific registered breakpoint system, `getBreakpointSystem("bulma")` returns a different breakpoint system that comes bundled with imola:

    Imola Breakpoint System
    Name:  bulma
    description:  No description

    Available Breakpoints (name)   Minimum screen size (px)   Maximum screen size (px)
    -----------------------------  -------------------------  -------------------------
    tablet                         769                        NULL
    desktop                        1024                       NULL
    widescreen                     1216                       NULL
    fullhd                         1408                       NULL
    -----------------------------

For a full list of all breakpoint systems we can use `listBreakpointSystems()`.

But how can we use these? Lets say we have the following gridPanel():

    gridPanel(
      areas = c(
        "area1 area1 area1",
        "area2 area3 area3",
        "area2 area3 area3"
      ),
      ...
    )

In out case, we want to use the default breakpoint system and target small devices, so we target these via `xs`, and build our `areas` argument as a named list instead.

We can use `default` as a name for our default value for the `areas` argument. Think of `default` as the value used for screen sizes where no other value can be applied.

`default` is a reserved keyword in imola, so keep in mind not to use it when editing or creating a custom breakpoint system.

    gridPanel(
      areas = list(
        default = c(
          "area1 area1 area1",
          "area2 area3 area3",
          "area2 area3 area3"
        ),
        xs = c(
          "area1",
          "area2",
          "area3"
        )
      ),
      ...
    )

Switching to a different breakpoint system would change this syntax slightly.

We can either change the default global system with `setActiveBreakpointSystem(name)` or change it for this specific `gridPanel()`.

Changing the global system would affect all panels that are currently using breakpoints, so picking a global system should be something you do before adding breakpoints to your arguments.

As a workaround we can define a different system for this panel only:

    gridPanel(
      areas = c(
        "area1 area1 area1",
        "area2 area3 area3",
        "area2 area3 area3"
      ),
      ...,
      breakpoint_system = getBreakpointSystem("bulma")
    )

And use the available names in that system instead:

    gridPanel(
      areas = list(
        tablet = c(
          "area1 area1 area1",
          "area2 area3 area3",
          "area2 area3 area3"
        ),
        default = c(
          "area1",
          "area2",
          "area3"
        )
      ),
      ...,
      breakpoint_system = getBreakpointSystem("bulma")
    )

In this case the bulma system is mobile first, meaning `default` will apply for mobile screen and `tablet` for any screen width above `769px`.

All `gridPanel()` and `flexPanel()` arguments that affect the styling of the panel allow this behavior for the use of breakpoints. See each function documentation for more details.

## Extending breakpoint systems

It is also possible to edit a breakpoint system and add or remove breakpoints. To do this we must first retrieve a registered system using `getBreakpointSystem(name)`.

This returns a object version of that system,

    > obj <- getBreakpointSystem()
    > obj

    Imola Breakpoint System
    Name:  bootstrap3
    description:  No description

    Available Breakpoints (name)   Minimum screen size (px)   Maximum screen size (px)
    -----------------------------  -------------------------  -------------------------
    xs                             NULL                       575
    sm                             NULL                       767
    md                             NULL                       991
    lg                             NULL                       1199
    xl                             1200                       NULL
    -----------------------------

We can now edit this system by adding or removing breakpoints. To create a new breakpoint lets call `breakpoint()`:

    > breakpoint("mybreakpoint", min = 300, max = 400)
    Imola Breakpoint
    Name:  mybreakpoint

    Affect Screen Sizes:
    Minimum:  300 px
    Maximum:  400 px

This creates a breakpoint that can than be added to our system with `addBreakpoint()`:

    > obj <- addBreakpoint(obj, breakpoint("mybreakpoint", min = 300, max = 400))
    Imola Breakpoint System
    Name:  bootstrap3
    description:  No description

    Available Breakpoints (name)   Minimum screen size (px)   Maximum screen size (px)
    -----------------------------  -------------------------  -------------------------
    xs                             NULL                       575
    sm                             NULL                       767
    md                             NULL                       991
    lg                             NULL                       1199
    xl                             1200                       NULL
    mybreakpoint                   300                        400
    -----------------------------

We can also remove a breakpoint by name using `removeBreakpoint()` This creates a breakpoint that can than be added to our system with `addBreakpoint()`:

    > obj <- removeBreakpoint(obj, "xl")
    Imola Breakpoint System
    Name:  bootstrap3
    description:  No description

    Available Breakpoints (name)   Minimum screen size (px)   Maximum screen size (px)
    -----------------------------  -------------------------  -------------------------
    xs                             NULL                       575
    sm                             NULL                       767
    md                             NULL                       991
    lg                             NULL                       1199
    mybreakpoint                   300                        400
    -----------------------------

After we finish editing our system we can now use it either in the object for as an argument:

    gridPanel(
      ...,
      breakpoint_system = obj
    )

Or register it for global usage:

    registerBreakpointSystem(obj)

    gridPanel(
      ...,
      breakpoint_system = getBreakpointSystem("bootstrap3")
    )

NOTE: Registering a breakpoint system will overwrite a system with the same name.

If you would like to unregister a breakpoint system, you can also use `unregisterBreakpointSystem()`.

## Creating a new breakpoint system

Breakpoint systems can also be created from scratch using `breakpointSystem()`

    > breakpointSystem("mysystem", breakpoint("mybreakpoint", min = 300, max = 400))
    Imola Breakpoint System
    Name:  mysystem
    description:  No description

    Available Breakpoints (name)   Minimum screen size (px)   Maximum screen size (px)
    -----------------------------  -------------------------  -------------------------
    mybreakpoint                   300                        400
    -----------------------------

All functionality to add or remove breakpoints, register and unregister the system, using it as a argument value can also be used with this object.

## Importing and Exporting

It is also possible to import and export breakpoint system objects for future usage in different projects. In this case you can make use of `exportBreakpointSystem()` and `importBreakpointSystem()`.

`exportBreakpointSystem()` allows you to export a system object into a specific file.

This file will contain all the necessary information to rebuild the system, even in a different project, and can be turned back into a template object using `importBreakpointSystem()`.

After importing all functionality to add or remove breakpoints, register and unregister the system, using it as a argument value can also be used with this object.

## Best Practices for adding Responsive Breakpoints

- Develop for mobile-first – By developing and designing mobile-first content, the developer and designer receive multiple benefits. It is more difficult to simplify a desktop experience for mobile screens than it is to expand a mobile view for desktop screens. When a design is mobile-first, developers address what is most necessary, and can then make additions to match the preferences of desktop users.

- Always keep major breakpoints in mind. This usually means common screen sizes (480px, 768px, 1024px, and 1280px).

- Before choosing major breakpoints, use website analytics to discern the most commonly used devices from which your site is accessed. Add breakpoints for those screen sizes first.

- An intelligent method is to hide or display elements at certain breakpoints. If necessary, switch content or features at breakpoints. For example, consider implementing off-canvas navigation for smaller screens and a typical navigation bar for larger ones.

- Don’t define standard breakpoints for responsive design on the basis of device size. The primary objective of responsive design breakpoints is to display content in the best possible way. So, let the content be the guide. Add a breakpoint when the content and design requires it.