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:


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:

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