Primer.css is the internal framework that GitHub uses to handle styles and organize its CSS. The primary purpose of Primer.css is to enforce consistent styles across a website. In this article, we will learn about Primer.css, how to use it, and what it takes from BEM. We will begin by learning about what it comprises before we build a landing page and evaluate its resemblance to BEM.
Jump ahead:
We can install Primer.css via npm or Yarn by running npm install @primer/css
. After that, set up Sass and import it into our main SCSS file with the @import "@primer/css/index.scss";
command.
Similarly, you can download the minified version and host it in your code via this link. However, for this article, we will use the CDN version, so make sure to create a new folder named primercss-cdn
. Then, create a new index.html
file and add the following link to the head
tag:
<link href="https://unpkg.com/@primer/css@^20.2.4/dist/primer.css" rel="stylesheet" />
Your code should look similar to this:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link href="https://unpkg.com/@primer/css@^20.2.4/dist/primer.css" rel="stylesheet" /> <title>Primer CSS cdn</title> </head> <body></body> </html>
Primer.css is a fantastic framework that has been divided into subsections covering utilities, components, and marketing styles. However, we need to understand how breakpoints, spacing, and theming work to get started with the subsections.
Like other popular CSS frameworks like Tailwind CSS, Primer.css uses four breakpoints that rely on a min-width
property set in the media query. The breakpoints are sm
, md
, lg
and xl
which represents a minimum width of 544px
, 768px
, 1012px
, and 1280px
.
To specify a breakpoint, you can use a syntax similar to setting the display on a small and medium screen. Primer.css uses a scale from 0-12
that represents values from 0
to 4px
, 8px
, 16px
, 24px
, 32px
, 40px
, 48px
, 64px
, 80px
, 96px
, 112px
and 128px
. To set spacing for an element, you can specify your desired axis like this: <h2 class="px-1 py-1">Content</h2>
.
These days, users have a list of basically two preferences for theming. These preferences are light and dark modes. Primer.css has provided a set of data attributes that can be used to set the theme for your website. These data attributes are expected to be added to the <html>
which is the root tag of the entire page. Here’s what they look like:
Theme | Data attributes |
Light |
<html data-color-mode="light" data-light-theme="light"> <!-- Other tags go in here --> </html> |
Dark |
<html data-color-mode="dark" data-dark-theme="dark"> <!-- Other tags go in here --> </html> |
Dark dimmed |
<html data-color-mode="dark" data-dark-theme="dark_dimmed"> <!-- Other tags go in here --> </html> |
Dark high contrast |
<html data-color-mode="dark" data-dark-theme="dark_high_contrast"> <!-- Other tags go in here --> </html> |
The Primer.css utility styles include colors
, box-shadows
, and CSS grids that are used most frequently in our code. Essentially, these styles help us maintain a more consistent style on our website. Primer.css has provided a set of utilities we can use, which we will discuss below.
Padding is used to specify the internal spacing of an element. Primer.css provides shorthands that can be used together to set padding. For instance, we can set the padding for all the sides of an element in combination with any of the spacing we discussed above like this: p-2
. Similarly, we can set the padding for the left, right, top, bottom, vertical, and horizontal axes using the following class names: pl-2
, pr-2
, pt-2
, pb-2
, py-2
, and px-2
.
Primer.css is focused on responsiveness, and staying in line with that goal, the Primer.css team has provided a set of classes for setting the padding for different screen sizes. All you have to do is to attach sm
, md
, lg
, or xl
to the padding. It should look similar to this: p-sm-2 py-md-3 pt-lg-4
.
Generally, with margins, we can set the spacing between an element and another element. The class names look very similar to padding except for replacing p
with m
. Moreover, the class name mx-auto
is used to center block
elements only when they have a set width while mb-auto
, ml-auto
, mr-auto
and mt-auto
are used to move a block
element to the top, right, left, and bottom.
It is also important to note that we can set negative margins with class names similar to mr-n6 m-n2 my-n9 my-md-n7
. The secret is to precede the letter spacing number with the letter n
.
Typography, in this case, includes the styles for setting text alignment, font size, line height, word break, list styles, typographic styles, and more. You can learn more about typography in Primer.css in the official docs.
The text alignment utility can be used alight the right, center, or left of the text and images inside an element using the following styles: text-right
, text-center
, and text-right
respectively. Heading and type scale utilities are used to specify the font size of a text or SVG.
The heading utilities set the font size
and the font-weight
to match the default heading styles. These styles range from h1
to h6
with the former being the largest and the later the smallest:
<p class="h4">I love primer.css</p>
The type scale only sets the font size and ranges from f1
to f6
. Here’s an example:
<p class="f5">I love my Life</p>
Primer.css comes with 13 colors to choose from, including: default
, muted
, subtle
, accent
, success
, attention
, severe
, danger
, open
, closed
, done
, sponsors
, and on
.
The CSS classes for setting colors are broadly divided into four parts: text
, background
, border
, and link
. The class name for text color is written like this: color-fg-danger
. Essentially, the last text can be any of the colors listed above, which includes success
, muted
, and more.
To use the on
text color, it should be written like this: color-fg-on-emphasis
. The background color is similar to the text color, as color-bg-subtle
. However, it has another deeper color set with emphasis
attached to its class name.
Meanwhile, the class name for setting border colors also resembles that of background colors with the following: color-border-open
, color-border-closed-emphasis
, and color-border-sponsors
.
However, Links
are a little bit different. It can allow classes for primary
, secondary
, muted
, and states such as hover
and nested
. The primary link can be written like this:
<a class="Link--primary" href="/home">Link--primary</a>
While the secondary link can be written like this:
<a class="Link--secondary" href="/home">Link--secondary</a>
Muted links can be indicated using the Link--muted
class. The on-hover
link class is used to make any text color used within the links to turn blue on hover. It can be used like this:
<a class="color-fg-secondary no-underline" href="#url"> This is a link <span class="Link--onHover">Link--onHover</span> </a>
Finally, the nested link is used to show emphasis on some text inside a link, as shown below:
<a class="color-fg-secondary no-underline" href="#url"> This is a link <span class="Link">Link</span> </a>
The class names for setting borders are divided into five parts: setting default borders, border style, border radius, border colors, and responsive borders. To set the default border, we can use any of the following class names such as: border
, border-left
, border-bottom
, border-right
, border-x
, and border-y
. Meanwhile, Primer.css allows attaching a scale of 0-3
to the border, such as border-2
. It is worth noting that the following class names: border-top-0
, border-right-1
, border-bottom-2
, and border-left-3
can be used to set the border for the edges of an element.
To set the border style of an element, the class name of border
must always be provided like this: border dashed
. Similarly, we can also provide the border radius of an element using the rounded
class name with a scale of 0-3
or the class name of circle
to make the element circular. It can be used like this:
<div class="border rounded-2> .border .rounded-2 </div>
Or:
<div class="border circle" style="width: 80px; height: 80px;"> .border .circle </div>
Finally, just like with padding and margin, we can also set the border of an element on different breakpoints. We can do it on any of the border patterns mentioned above, such as: border-sm
, border-md
, border-lg
, border-xl
, border-sm-2
, border-sm-top
, and border-sm-bottom-3
.
Primer.css provides five class names to control the box shadow of an element, including: color-shadow-small
, color-shadow-medium
, color-shadow-large
, color-shadow-extra-large
, and box-shadow-none
.
It can be used as follows:
<div class="color-shadow-extra-large p-3"> .color-shadow-extra-large </div>
The class name for handling CSS flexbox essentially reflects what the style does. To make an element a flexbox, we can use d-flex
and d-inline-flex
which stands for:
{ display: flex; }
And:
{ display: inline-flex; }
Meanwhile, you can use the flex-row
, flex-row-reverse
, flex-column
, and flex-column-reverse
classes to set the flex directions. Interestingly, the same naming convention exists for flex-wrap
. This means that you can use flex-wrap
, flex-nowrap
, and flex-wrap-reverse
to choose whether flex items should be wrapped into multiple lines when they overflow or into a single line.
It’s worth noting that you should have already made an element a flexbox before you can apply the other flex classes like this:
<div class="border d-flex flex-wrap"> <span class="p-4 border">Item 1</span> <span class="p-4 border">Item 2</span> <span class="p-4 border">Item 3</span> </div>
To apply the justify-content
style that is used to distribute the space around and between flex items, we can use the following class names: flex-justify-start
, flex-justify-end
, flex-justify-center
, flex-justify-between
, and flex-justify-around
.
Furthermore, you can use the following class names to apply the align item
style: flex-justify-around
, flex-items-end
, flex-items-end
, flex-items-baseline
, and flex-items-stretch
.
Although we can create simple and complex layouts using the styles above, there may be times when we want a bit more control over our flex items. The flex-1
class name instructs a flex item to fill up any available space. The flex-auto
class can be used to make a flex item fill up open space and auto-resize it if its content changes. While the flex-grow-0
and flex-shrink-0
can be used to prevent the growing and shrinking of a flex item.
Other class names provided by Primer.css are used to adjust the alignment of an individual flex item:
.flex-content-start { align-content: flex-start; } .flex-content-end { align-content: flex-end; } .flex-content-center { align-content: center; } .flex-content-between { align-content: space-between; } .flex-content-around { align-content: space-around; } .flex-content-stretch { align-content: stretch; }
And to set the order:
.flex-order-1 { order: 1; } .flex-order-2 { order: 2; } .flex-order-none { order: inherit; }
We can even add and apply a responsive style to items by appending any breakpoints to the flexbox class names mentioned above. It generally follows two rules:
d-[sm or md or lg or xl]-[the property]
flex-[sm or md or lg or xl]-[the property]-[the behavior]
An excellent example of this is setting an item to a flexbox in a particular breakpoint like this: d-md-flex
; or specifying justified content style for only a breakpoint: flex-lg-justify-center
.
Components in Primer.css are already-built items that look the same regardless of where they are placed on the webpage. They follow patterns that only work when you combine the appropriate modifiers and class names.
Buttons
The Primer.css default button
component can be created by adding btn
class to a button
element, as shown below:
<button class="btn" type="button">Default button>
Essentially, we can add more classes to a button
to set its type, state, and variations. The class names that can be used to specify the button type are btn-primary
, btn-outline
, and btn-danger
. As for the state, we can use the aria-disabled="true"
and aria-selected="true"
attributes to disable or enable a button. The attributes can be used as follows:
<button class="btn mr-2" type="button" aria-disabled="true">Disabled
Also, we can specify btn-sm
or btn-large
to set the size of the button:
<button class="btn btn-large">Large button</a>
Similarly, we can make a button grow to the width of its parent when we set btn-block
class name to it and remove the borders around the button using the btn-invisible
class name.
Alerts
Alerts are used to inform users about the status of their actions. In Primer.css, alerts can be created by adding the flash
class name to an element. By default, this will generate an alert with a light blue background. However, we can vary the alert by appending the flash-warn
, flash-error
, or flash-success
classes.
Most of the time, alerts are dismissable and have a close
button. We can use the flash-close js-flash-close
to properly add to it and position the icon for the action. To create the UI of a dismissible alert, your markup should look similar to this:
<div class="flash mt-3 flash-error"> Alert message <button class="flash-close js-flash-close" type="button" aria-label="Close" > Close </button> </div>
Box
and Loaders
A Box
in Primer.css is a container with a background color, borders, and rounded corners. It has no extra styles by default. To create a Box
, we can use the Box
class:
<div class="Box"> Box Content </div>
A Box
can have a header, body, and footer. The following class names can be used to indicate the function of an element inside a box: Box-header
, Box-body
, and Box-footer
. We can use the class name to add emphasis on the title of a Box
.
We can use modifiers to further enhance the look and feel of our Box
. The Box-header--blue
can be used to change the background and border color of an element with the Box-header
class name:
<div class="Box"> <div class="Box-header Box-header--blue"> <h3 class="Box-title">This is the title</h3> </div> <div class="Box-body"> This is Box body </div> </div>
Other modifiers, such as Box--danger
and Box-row--unread
can be set to the Box
container and the Box
item. They essentially add a red border to the Box
and a left border to the Box
item, respectively.
Loaders
are used to indicate a pending state. It should be used with other components and can be triggered using the AnimatedEllipsis
class name:
<h2><span>Loading</span><span class="AnimatedEllipsis"></span></h2>
In this section, we will build a landing page that we can use to demonstrate how to combine components and utilities available in Primer.css. Here is what the result will look like:
The first step in building our landing page is to open the primercss-cdn/index.html
that we created earlier. Then, we will create a div
that will wrap our app:
<div style="max-width: 1200px" class="mx-auto py-1 px-2"> <!-- Content will go here --> </div>
Next, we will add our navbar:
<div> <nav class="UnderlineNav" aria-label="Nav bar"> <div class="UnderlineNav-body"> <a class="UnderlineNav-item app-link" href="#home" aria-current="page" onclick="handleClick(this)" >Home</a > <a class="UnderlineNav-item app-link" href="#about" onclick="handleClick(this)" >About</a > <a class="UnderlineNav-item app-link" href="#newsletter" onclick="handleClick(this)" >Newsletter</a > </div> <div class="UnderlineNav-actions"> <a href="mailto:[email protected]" class="btn btn-sm">Reach Us</a> </div> </nav> </div>
Let’s break it down:
Our navbar is made up of a div
that wraps a nav
element. The nav
contains an UnderlineNav
class and wraps two div
with an UnderlineNav-body
and UnderlineNav-actions
that signify two parts of the navbar.
Meanwhile, the UnderlineNav-body
contains three a
elements that all have the UnderlineNav-item
class from Primer.css, app-link
class, and aria-current="page"
that we will be used to show the currently selected item. Also, each item has an onClick
handler, which we will handle with JavaScript.
hero
sectionAfter that, we can add our hero
section:
<div id="hero" class="py-8 blankslate"> <div style="width: 90%; max-height: 200px" class="mx-auto border p-3 rounded-2" > <h1>I love Primer.css</h1> <p>Click on the buttons below to find out why:</p> <div class="d-flex flex-wrap px-3 my-4" style="gap: 0.5rem"> <!-- Pop up example --> <div class="position-relative text-center"> <div class="Popover position-relative app-popover" style="display: none" > <div class="Popover-message Popover-message--bottom p-4 mx-auto mb-2 text-left Box color-shadow-large" > <h4 class="mb-2">Yes, this is the heading</h4> <p>You are not out of luck! This is the message</p> <button type="submit" class="btn btn-outline mt-2 text-bold app-popover-btn" onclick="handlePopover()" > Got it! </button> </div> </div> <button class="btn btn-primary" onclick="handlePopover()"> View Popup </button> </div> <!-- End Pop up example --> <!-- Loading example --> <div class="position-relative text-center"> <div class="Popover position-relative app-popover2" style="display: none" > <div class="Popover-message Popover-message--bottom p-4 mx-auto mb-2 text-left Box color-shadow-large" > <h2> <span>Loading</span ><span class="AnimatedEllipsis mb-2"></span> </h2> <button type="submit" class="btn btn-outline mt-2 text-bold app-popover-btn" onclick="handlePopover2()" > Got it! </button> </div> </div> <button class="btn-mktg btn-small-mktg mx-3" type="button" onclick="handlePopover2()" > View Others </button> </div> <!-- End loading example --> <!-- Dialog Example --> <details class="details-reset details-overlay details-overlay-light" > <summary class="btn" aria-haspopup="dialog">Open dialog</summary> <details-dialog class="Box Box--overlay d-flex flex-column anim-fade-in fast" > <div class="Box-header"> <button class="Box-btn-octicon btn-octicon float-right" type="button" aria-label="Close dialog" data-close-dialog > <!-- <%= octicon "x" %> --> <svg class="octicon octicon-x" viewBox="0 0 12 16" version="1.1" width="12" height="16" aria-hidden="true" > <path fill-rule="evenodd" d="M7.48 8l3.75 3.75-1.48 1.48L6 9.48l-3.75 3.75-1.48-1.48L4.52 8 .77 4.25l1.48-1.48L6 6.52l3.75-3.75 1.48 1.48L7.48 8z" </path> </svg> </button> <h3 class="Box-title">What do you think about this?</h3> </div> <div class="overflow-auto"> <div class="Box-body overflow-auto"> <p> You see how cool this CSS framework is?? <br /> It is what it is! </p> </div> <ul> <li class="Box-row"> <img class="avatar v-align-middle mr-2" src="https://avatars.githubusercontent.com/bonarhyme?s=48" alt="bonarhyme" width="24" height="24" /> @bonarhyme </li> <li class="Box-row border-bottom"> <img class="avatar v-align-middle mr-2" src="https://avatars.githubusercontent.com/bonarhyme?s=48" alt="bonarhyme" width="24" height="24" /> @bonarhyme </li> <li class="Box-row border-bottom"> <img class="avatar v-align-middle mr-2" src="https://avatars.githubusercontent.com/bonarhyme?s=48" alt="bonarhyme" width="24" height="24" /> @bonarhyme </li> </ul> </div> <div class="Box-footer"> <button type="button" class="btn btn-block" data-close-dialog> Close </button> </div> </details-dialog> </details> <!-- End of Dialog example --> </div> </div> </div>
Let’s break it down:
The hero page is made up of a div
that contains a hero
ID and py-8 blankslate
class names. It wraps around another div
with the following styles and class names:
style="width: 90%; max-height: 200px" class="mx-auto border p-3 rounded-2"
The inner div
contains an h1
, p
, and a div
with the following style and classes:
class="d-flex flex-wrap px-3 my-4" style="gap: 0.5rem"
Inside the div
we have a popup, loading, and dialog example. The secret to creating a popup with Primer.css is adding the position-relative text-center
class to the element you want to make a popup. Then, it should have the div
and button
children that will be used to display the contents and trigger the popup. For our example, our button
should have the following attributes:
class="btn btn-primary" onclick="handlePopover()"
The div
containing the content should have the following attributes:
class="Popover position-relative app-popover" style="display: none"
It should also have an inner div
of the classes: Popover-message Popover-message--bottom p-4 mx-auto mb-2 text-left Box color-shadow-large
that will eventually contain the contents. We can also add an onclick
to a button
inside the Popover-message
with onclick="handlePopover()"
.
Next, we will add the about us section to our landing page. The about section contains an h2
that is actually a tooltip. It also includes another div
with a colorful timeline
with four items in it. For brevity, here’s the link to the GitHub repo.
The first timeline
item contains breadcrumbs
. While the second, third, and fourth timelines have truncated links, normal links with a hover state style, and a toast
component.
Next, we have the newsletter
section which we can add with the following:
<div id="newsletter"> <h2 class="py-2 h3-mktg">Subscribe to my Newsletter</h2> <form> <div class="form-group"> <div class="form-group-header"> <label for="example-text">Email address</label> </div> <div class="form-group-body"> <input class="form-control" type="text" placeholder="Email address" id="example-text" /> </div> </div> <button class="btn btn-block btn-primary" style="max-width: 440px"> Submit </button> </form> </div>
Let’s break it down. The newsletter
section contains an h2
and a form
. The form
contains a label
, input
, and a button
. Next, we have the footer, as shown below:
<div> <p class="text-center py-4"> Made by <a href="https://github.com/bonarhyme">bonarhyme</a> </p> </div>
Finally, we have our script
that handles onclick
events in our code:
// Internal JavaScript written inside the body of HTML <script> function handleClick(e) { document.querySelectorAll('.app-link').forEach((x) => { x.removeAttribute('aria-current'); }); e.setAttribute('aria-current', 'page'); } function handlePopover() { const popup = document.querySelector('.app-popover'); if (popup.style.display === 'none') { popup.style.display = 'block'; } else { popup.style.display = 'none'; } } function handlePopover2() { const popup = document.querySelector('.app-popover2'); if (popup.style.display === 'none') { popup.style.display = 'block'; } else { popup.style.display = 'none'; } } </script>
Primer.css has a unique architecture that uses object-oriented CSS programming techniques and is influenced by the BEM model. BEM stands for block, element, and modifiers. Essentially, BEM is a method of writing CSS and structuring HTML that helps create components that are cleaner and easier to share across the team. To learn more about BEM and its methodologies, check out this article.
Generally, a block is a standalone entity or component that stands and is meaningful on its own. A great example of a block is a menu, navigation, box, and so on. It should be written like this:
<div class=”card”> <!-- elements and modifiers go here –> </div>
An element is part of a block semantically tied to its block. Mostly, it has no standalone meaning and should always be tied to a block. Navigation items, box headers, and menu items are all great examples of an element. It can be written with double underscore like so:
<div class="navigation__item">A navigation item</div>
Or like this:
<div class="card__header">This is a card header</div>
As the name implies, a modifier is used to change the behavior and appearance of a block or an element. This means that a modifier can be tied directly to a block or an element. Examples of applying a modifier to a block include: navigation disabled
, box danger
, button large
, etc. An example of appending a modifier to an element includes: navigation item disabled
, box header danger
, and more. Generally, a modifier should be written like this:
<div class=”card__button–danger”>A card button that indicates an error that has occurred</div>
Furthermore, The syntax for writing BEM using a card as an example is as follows:
<section class="card"> <h1 class="card__title">The Title</h1> <div class="card__imageContainer"> <img src="https://images.unsplash.com/photo-1676136358787-6510632d3e30?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=387&q=80" alt="an amazing landscape" class="card__image" /> </div> <div class="card__content"> <p class="card__description"> Lorem ipsum dolor sit amet consectetur adipiscing elit. Aperiam porro quidem iure natus consequatur. A dolorum quis mollitia veritatis nostrum excepturi eligendi impedit doloremque, autem non reiciendis nemo, error quidem. </p> <div class="card__buttonGroup"> <button class="card__button">Proceed</button> <button class="card__button card__button--disabled">Cancel</button> <button class="card__button card__button--danger">Delete</button> </div> </div> </section>
And CSS:
.card { width: 100%; max-width: 400px; margin-left: auto; margin-right: auto; border: 1px solid gray; padding: 0.8rem; border-radius: 0.5rem; } .card__title { color: rgb(31, 31, 31); } .card__imageContainer { width: 100%; height: 300px; } .card__image { width: 100%; height: 100%; object-fit: cover; } .card__buttonGroup { display: flex; gap: 0.5rem; } .card__button { padding: 0.4rem 0.8rem; cursor: pointer; outline: none; border-radius: 0.3rem; border: 1px solid; } .card__button--disabled { cursor: not-allowed; background-color: gray; border-color: gray; } .card__button--danger { background-color: rgb(231, 36, 36); color: white; border-color: rgb(231, 36, 36); }
Primer.css makes use of the same pattern BEM uses. However, instead of double underscores, which are used to identify elements, it uses a hyphen. But for modifiers, it makes use of an ARIA for button
components and a double hyphen for some other components, like a Box
.
A block in Primer.css includes components such as Dropdowns
, Pagination
, Popovers
, and menu
. It should be noted that some blocks are capitalized while some are lowercase. With BEM, elements shouldn’t be standalone and should instead be inside of a block. The same applies to Primer.css; an element must be inside a component to work as expected. Also, modifiers must be combined with a block or an element to work correctly.
BEM methodology ensures that our CSS code is more structured and easily reused across the team in various application parts by enforcing a more consistent pattern of generating class names. This particular approach is what Primer.css uses under the hood to ensure that our app UI is consistent across the board.
The simplicity of Primer.css makes it an excellent choice for building your next project and should be adopted because it comes with many components and utilities that are very responsive and clean. Thank you for reading through. I hope you enjoyed this article, and be sure to comment if you have any questions. Happy coding!
As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.
Modernize how you debug web and mobile apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]