窓 (mado)
UI

A collection of opinionated React components built on Tailwind.

What is a 窓?

窓 (mado), means window in Japanese. We want our window to be large, clear, and as clean as possible.

a really big, clear, clean 窓

Component library

There are many components that are intended to make your life as a developer easier. Stop trustingAI to design your components, and stop waiting for designers to finish their work—and, the worst option, stop thinking you're good at UI/UX design… Alright, you might actually be good at UI/UX design, so each component is easy to adjust to your liking, without losing some particularly useful defaults.

Now, let's say you're not great at UI/UX design, but want to get better. Good! Please support the folks at Tailwind by purchasing their book: Refactoring UI. Without Tailwind, 窓 UI would not be possible.

Getting Started

Installation

npm install @andrilla/mado-ui

pnpm add @andrilla/mado-ui

yarn add @andrilla/mado-ui

bun add @andrilla/mado-ui

deno add npm:@andrilla/mado-ui

Be sure to install the required peer dependencies:

npm install react tailwindcss use-context-selector scheduler

pnpm add react tailwindcss use-context-selector scheduler

yarn add react tailwindcss use-context-selector scheduler

bun add react tailwindcss use-context-selector scheduler

deno add npm:react npm:tailwindcss npm:use-context-selector npm:scheduler

The first thing you need to do is add the CSS to your project (likely in a globals.css file).

@import "@andrilla/mado-ui/css";

General Usage

Other than components there are four other exported directories.

/css
Provides all of the necessary CSS variables and classes for styling. This must be paired with Tailwind CSS in your globals.css.
/graphics
Provides graphical assets that you may find useful. The primary purpose is social media logos, including Facebook, Instagram, X, YouTube, LinkedIn, TikTok, and Google.
/hooks
Provides useful React hooks for common tasks, including color scheme detection, form status context tracking, mobile device detection, and pointer movement.
/utils
Provides helpful utility functions for common tasks, including class management (CSS classes on elements), customizing tailwind merge, date object reading and formatting, React helpers (get static text from children without requiring the client), regular expression checks, and various string manipulation utilities.

Detailed Usage

Be sure to scroll back up to see detailed usage of components.

CSS

Tailwind Merge

Tailwind Merge is built in and extended in this project, to support all of the additional utilities and changes we made.

There is no need to install tailwind-merge. You can simply import it from this project.

import { twMerge } from '@andrilla/mado-ui/utils'

However, twJoin() is not supplied. Instead of installing tailwind-merge, we recommend joining an array of strings.

1
2
3
4
5
6
7
8
9
10
export function MyComponent() {
	return (
		<div
			className={[
				'bg-blue-500',
				'text-red-500',
			].join(' ')}
		/>
	)
}

If you need to extend tailwind-merge, you can import extendMadoTailwindMerge() and use it just as with extendTailwindMerge().

import { extendMadoTailwindMerge } from '@andrilla/mado-ui/utils'

Tailwind CSS Changes

To help create better design with less work, there are some changes to the Tailwind base that you should be aware of. There are also some additions to the theme and some helpful utility classes that may prove useful.

Directional intent

It's important to understand the intent behind many of these changes. We believe modern CSS to moving toward almost exclusively logical properties. Even Tailwind changed px-* and py-* to use logical properties. In light of this, many of these changes take current utility classes and pair them with logical properties, or add logical property features.

We also believe design systems should be interconnected, meaning properties rely on each other for their values. This means changing one property automatically aligns related properties, without you needing to update them all individually. This helps massively with maintainability as well.

Lastly, although Tailwind includes almost any property you might need, we believe creating unique design systems each time, requires more options—some of which utilize features that not every browser supports.

Base @layer base

  • All elements receive relative position, since intentionally using position: absolute to position an element outside of its parent is a rarity, and can be easily changed with the static class. This also helps avoid confusion when some elements default to relative instead of static.

    All elements receive no minimum width, because browsers include min-width by default, on elements like <input> which makes them confusing to work with

    * {
    	position: relative;
    	min-width: 0;
    }
  • All headings, even without using the Heading component, receive balanced text wrapping.

    h1,
    h2,
    h3,
    h4,
    h5,
    h6 {
    	text-wrap: balance;
    }
  • Body text receives a lighter opacity to provide distinction from headings, and pretty text wrapping.

    p {
    	color: color-mix(
    		in oklab,
    		currentcolor 80%,
    		transparent
    	);
    	text-wrap: pretty;
    }
  • Buttons use the pointer cursor, except when disabled.

    button:not(:disabled):not([aria-disabled='true']) {
    	cursor: pointer;
    }
  • Fill and stroke colors on SVG elements match the current color, making it easier to pair with text. stroke-width: 0 is only here because settings a stroke color makes the stroke width 1. It will not override stroke widths set on SVGs.

    svg {
    	fill: currentcolor;
    	stroke: currentcolor;
    	stroke-width: 0;
    }
  • Images and videos use logical sizing.

    img,
    video {
    	max-width: none;
    	height: auto;
    	max-inline-size: 100%;
    	block-size: auto;
    }

Theme @theme

There's a lot in the theme, that relates to utilities. See the utilities section for those theme variables.

  • We built components with color around the --theme-color custom property, making it really easy to change the color, per use, on the fly. Most of the time, however, you just need the theme color to match your brand. To do this, just change the --base-theme-color custom property.

    Along with the base theme color, there are two connected custom properties: --base-theme-color--foreground and --base-theme-color--accent. These are used to set the foreground and accent colors of the theme, respectively. Most of the time, you should only change the base theme color, but if you need more control, you can change each separately.

    --base-theme-color: var(--color-blue-500);
    --base-theme-color--foreground: color-mix(
    	in oklab,
    	var(--base-theme-color) 5%,
    	var(--color-white)
    );
    --base-theme-color--accent: color-mix(
    	in oklab,
    	var(--base-theme-color) 90%,
    	var(--color-white)
    );
  • We often found that Tailwind's largest container size, --container-7xl, wasn't quite large enough, so we added --container-8xl to the theme. This is the same size as the 2xl: media query.

    --container-8xl: 96rem; /* 1536px */
  • Transition timing functions are good by default, but we wanted them to be much more extensive, to help fit any scenario. All of the timing functions are provided by Anime JS. Here is a list of all of the available timing functions (prepend with ease- as you would expect).

    • in
    • in-back
    • in-bounce
    • in-circ
    • in-elastic
    • in-expo
    • in-out
    • in-out-back
    • in-out-bounce
    • in-out-circ
    • in-out-elastic
    • in-out-expo
    • in-out-sine
    • in-sine
    • irregular
    • irregular-light
    • irregular-heavy
    • out
    • out-back
    • out-bounce
    • out-circ
    • out-elastic
    • out-expo
    • out-in
    • out-in-back
    • out-in-bounce
    • out-in-circ
    • out-in-elastic
    • out-in-expo
    • out-in-sine
    • out-sine
    • power-in
    • power-in-out
    • power-out
    • power-out-in
    • spring
    • spring-bouncy
    • spring-snappy
    • spring-strong
    • steps-start
    • steps-start

    And on the note of timing functions, we changed the default timing function to out-expo. This is because it provides an instant-response feeling that still feels smooth. Despite this default, please consider which timing function feels best for your brand, and use it as the default.

    --default-transition-timing-function: var(--ease-out-expo);
  • Unless your brand calls for it, avoiding absolute white and black is ideal, so we added these helpful custom properties, for your convenience. They are used in a few components, when a background or text color is needed, without inheriting from an ancestor.

    --global-bg: light-dark(var(--color-neutral-50), var(--color-neutral-900));
    --global-text: color-mix(
    	in oklab,
    	var(--global-bg) 10%,
    	contrast-color(var(--global-bg))
    );

    Changing them to fit your brand is ideal. The best way to do this is tinting the neutral color palette with your brand's primary color. If you prefer, you can change them directly. Changing only the --global-bg will automatically change the --global-text with an appropriately contrasting color.

    Something that we chose to not include by default is use of these properties on the body, but we highly recommend you add this to your globals.css.

    @layer base {
    	body {
    		color: var(--global-text);
    		background-color: var(--global-bg);
    	}
    }
  • The text-* utility for changing font-size has been extended to include relative sizing. This is useful when you want a span to be smaller or larger without having to set it manually, especially if the parent changes font-size across multiple breakpoints.

    --text-smaller: smaller;
    --text-larger: larger;

Utilities

This section is split into two categories: Extensions and Additions. Extensions are additional value or changes to Tailwind's default utilities. Additions are new utilities that are not part of Tailwind.

Extensions

Most of these extensions are adding values that Tailwind is very likely to support natively in the future, while others are there to make your life easier.

  • Container Changes

    Tailwind deliberately removed margin-inline: auto and padding-inline: 2rem in v4, in order to make the container more generic. Although it makes sense for Tailwind to ship like this, we found the container class to be unhelpful, since it only changes the max-width at each breakpoints.

    We believe it is more effective to have a consistent design choice for spacing, so we opted for changing the padding (block and inline separately) at each breakpoint. We also change the max-width to none and opt for max-inline-size: var(--container-8xl); /* 96rem = 1536px */

    max-inline-size: var(--container-8xl); /* 96rem = 1536px */

    Generally, when a sibling container is present, the padding should be merged, to avoid double spacing. To solve this, we use &:has(+ .container) to remove the padding-block-end from the element. If you have back-to-back containers with different background colors, add the .keep-block-end class to the container first to keep the padding.

  • Gap Utilities

    Since inheritance in CSS requires the parent element to have a defined value for the property, we added some helpful custom properties to make it easier to maintain consistent spacing.

    .gap-4 {
    	--tw-gap: calc(var(--spacing) * 4) /* 1rem */;
    	--tw-gap-x: calc(var(--spacing) * 4) /* 1rem */;
    	--tw-gap-y: calc(var(--spacing) * 4) /* 1rem */;
    	gap: calc(var(--spacing) * 4) /* 1rem */;
    }
    
    .gap-x-4 {
    	--tw-gap-x: calc(var(--spacing) * 4) /* 1rem */;
    	column-gap: calc(var(--spacing) * 4) /* 1rem */;
    }
    
    .gap-y-4 {
    	--tw-gap-y: calc(var(--spacing) * 4) /* 1rem */;
    	row-gap: calc(var(--spacing) * 4) /* 1rem */;
    }
  • Grid Template Utilities

    Tailwind uses a standard repeat(* 1fr) value for grid-cols-* and grid-rows-* utilities. This means that you can't properly transition between them without arbitrary values.

    Until interpolate-size: allow-keywords is a baseline feature for a few years, we use grid template properties to smoothly transition height or width between 0 and auto values. To assist with this, we added support for 0fr and 1fr values, without need for arbitrary declarations.

    .grid-rows-0fr {
    	grid-template-rows: 0fr;
    }
    
    .grid-cols-1fr {
    	grid-template-columns: 1fr;
    }

    Likewise, with its commonality, we found it useful to add transition utilities for each.

    .transition-cols {
    	transition-property: grid-template-columns;
    }
    
    .transition-rows {
    	transition-property: grid-template-rows;
    }
  • Grid Lanes

    Although display: grid-lanes is experimental, we do expect it to become a baseline feature in the future. Since it's also almost identical to display: grid in it's functionality, we found it easy to provide progressive enhancement for it.

    .grid-lanes {
    	display: grid;
    
    	@supports (display: grid-lanes) {
    		display: grid-lanes;
    	}
    }
  • Size Utilities

    This extension is to assist in our prioritization of logical properties. First, we added support for logical viewport units: vb, vi, dvb, dvi, lvb, lvi, svb, and svi. This applies to block-*, inline-* and size-* utilities.

    In addition to these, we extended the size-* utility to use logical properties, instead of width and height.

    Realistically, none of this should affect existing layouts.

Additions

We've added support for a few CSS properties that have been around for a while, and some that are experimental for progressive enhancement.

  • Animation Utilities

    A whole fleet of animation utilities have been added so you can easily adjust animation properties on the fly, without having to change the animation declaration itself.

    • animation-delay-*
      This accepts an integer value in milliseconds.
      .animation-delay-500 {
      	--tw-animation-delay: calc(500 * 1ms) /* 500ms */;
      	animation-delay: var(--tw-animation-delay);
      }
    • animation-duration-*
      This accepts an integer value in milliseconds. It sets the value to --tw-animation-duration. You may also provide a modifier to adjust the delay.
      .animation-duration-500 {
      	--tw-animation-duration: calc(500 * 1ms) /* 500ms */;
      	animation-duration: var(--tw-animation-duration);
      }
      .animation-duration-500\/250 {
      	--tw-animation-duration: calc(500 * 1ms) /* 500ms */;
      	--tw-animation-delay: calc(250 * 1ms) /* 250ms */;
      	animation-duration: var(--tw-animation-duration);
      	animation-delay: var(--tw-animation-delay);
      }
    • animation-direction-*
      This controls the animation direction, only accepting the valid CSS property values.
      .animation-direction-alternate {
      	--tw-animation-direction: var(--animation-direction-alternate) /* alternate */;
      	animation-direction: var(--tw-animation-direction);
      }
    • animation-ease-*
      This operates the same as ease-*, but for animation-timing-function and it uses whatever timing functions are registered in the theme.
      .animation-ease-in-out {
      	--tw-animation-timing-function: var(--ease-in-out) /* cubic-bezier(0.7, 0.1, 0.5, 0.9) */;
      	animation-timing-function: var(--tw-animation-timing-function);
      }
    • animation-fill-*
      This controls the animation fill mode, only accepting the valid CSS property values.
      .animation-fill-both {
      	--tw-animation-fill-mode: var(--animation-fill-both) /* both */;
      	animation-fill-mode: var(--tw-animation-fill-mode);
      }
    • animation-iteration-*
      This controls the animation iteration count, accepting infinite or an integer value.
      .animation-iteration-infinite {
      	--tw-animation-iteration-count: var(--animation-iteration-infinite) /* infinite */;
      	animation-iteration-count: var(--tw-animation-iteration-count);
      }
    • animation-start-*
      This controls the animation range start, accepting valid CSS property values or a number (pixel) or percentage value.
      .animation-start-50\/% {
      	--tw-animation-range-start: 50%;
      	animation-range-start: var(--tw-animation-range-start);
      }
    • animation-state-*
      This controls the animation play state, only accepting the valid CSS property values.
      .animation-state-paused {
      	--tw-animation-state: var(--animation-state-paused) /* paused */;
      	animation-play-state: var(--tw-animation-state);
      }
  • A New Container Utility

    In addition to changes to the existing container class, there is a new container-standalone utility that is intended for smaller containers, generally within containers.

    .container-standalone {
    	padding: calc(var(--spacing) * 6); /* 1.5rem = 24px */
    
    	@media (width >= 40rem /* 640px */) {
    		padding: calc(var(--spacing) * 8); /* 2rem = 32px */
    	}
    
    	@media (width >= 48rem /* 768px */) {
    		padding: calc(var(--spacing) * 10); /* 2.5rem = 40px */
    	}
    
    	@media (width >= 64rem /* 1024px */) {
    		padding: calc(var(--spacing) * 12); /* 3rem = 48px */
    	}
    
    	@media (width >= 80rem /* 1280px */) {
    		padding: calc(var(--spacing) * 16); /* 4rem = 64px */
    	}
    
    	@media (width >= 96rem /* 1536px */) {
    		padding: calc(var(--spacing) * 24); /* 6rem = 96px */
    	}
    }
  • Counter Utilities

    Due to its rarity of use, we found counter properties slightly cumbersome to remember and use with arbitrary Tailwind declarations. These utilities make it easier to declare and read counter properties, hopefully increasing their use.

    • counter-*
      This is the primary, counter-increment property. It accepts an arbitrary value, which is the name of the counter. Tailwind does not currently offer support for string values, outside of arbitrary values, like they use for data-* and aria-* variants.
      .counter-\[list\] {
       counter-increment: list;
      }

      The counter-* utility also accepts an integer modifier, for when you need to increment by a specific value.

      .counter-\[list\]\/4 {
      	counter-increment: list 4;
      }
    • counter-reset-*
      This is sets the counter-reset property. It works the exact same way as the counter-* utility, but for resetting the counter value.
      .counter-reset-\[list\] {
      	counter-reset: list;
      }

      It also accepts an integer modifier, for when you need to set it to a specific value.

      .counter-reset-\[list\]\/8 {
      	counter-reset: list 8;
      }
  • Text Box Utility

    One simple utility for trimming the text box to the typeface's capital letter height, and alphabetic baseline.

    .trim-both {
    	text-box-edge: cap alphabetic;
    	text-box-trim: trim-both;
    }
  • Corner Shape Utilities

    This is for using all of the upcoming corner shape properties. This is not a baseline feature yet, but it is expected to not an experimental feature, either.

    The corner-* utilities work just like rounded-* utilities, where you can choose which corner(s) it applies to.

    Each utility class accepts either a number or keyword value. When setting a number, it utilizes the superellipse() function. You may also use -corner-* to set negative values. Although not a standard keyword value for corner-shape, you may use corner-infinity (or -corner-infinity) to apply corner-shape: superellipse(infinity).

    • corner-*
      This is the primary utility for corner shape. It sets the corner shape for every corner of the element.
    • corner-s-*
      This sets corner-start-start-shape and corner-end-start-shape.
    • corner-e-*
      This sets corner-start-end-shape and corner-end-end-shape.
    • corner-bs-*
      This sets corner-start-start-shape and corner-start-end-shape.
    • corner-be-*
      This sets corner-end-start-shape and corner-end-end-shape.
    • corner-t-*
      This sets corner-top-left-shape and corner-top-right-shape.
    • corner-r-*
      This sets corner-top-right-shape and corner-bottom-right-shape.
    • corner-b-*
      This sets corner-bottom-left-shape and corner-bottom-right-shape.
    • corner-l-*
      This sets corner-bottom-left-shape and corner-top-left-shape.
    • corner-ss-*
      This sets corner-start-start-shape.
    • corner-se-*
      This sets corner-start-end-shape.
    • corner-es-*
      This sets corner-end-start-shape.
    • corner-ee-*
      This sets corner-end-end-shape.
    • corner-tl-*
      This sets corner-top-left-shape.
    • corner-tr-*
      This sets corner-top-right-shape.
    • corner-br-*
      This sets corner-bottom-right-shape.
    • corner-bl-*
      This sets corner-bottom-left-shape.
  • Fit Tolerance Utility

    This is for pairing with display: grid-lanes, which is not a baseline feature. Be aware, when using this that it is subject to change, as this is an experimental feature that was originally flow-tolerance.

    The flow-tolerance-* utility accepts an integer value (em), arbitrary value, or max, which results in calc(Infinity * 1em).

    .flow-tolerance-2 {
    	fit-tolerance: calc(2 * 1em) /* 2em */;
    }

Custom Variants

It's likely you'll never use these, since browser support is limited, but sometimes, you may find them useful. These custom variants are self explanatory. transparency-safe, transparency-reduce, not-transparency-safe, not-transparency-reduce.

Graphics

Why are these here?

These graphics are the most common on the web. We include them in this package not only because of the commonality, but to provide helpful utilities to make them fit your brand.

Social Media Logos

Each logo is served in SVG format in its primary brand colors. Some have additional props to customize their appearance within the brand's acceptable use. You can also change the fill color to match your brand's color scheme.

Facebook

Facebook allows you to display their logo in a circle (default) or just as the f. To pick, use the variant prop.

By default, both variants use Facebook's primary gradient. To customize the color, set the gradient prop to false.

With the circle variant, you also have options for targeting props of specific SVG elements and choosing whether the f is cut out or filled in (default).

When cutout is set to false (or unset), the targets prop offers background and fill. When cutout is set to true, targets only offers background.

In addition to changing props of specific SVG elements directly, you can also style them using CSS. Each target has data-nameattribute with its target name.

Google

Google's primary logo utilizes gradients, which are provided within the SVG. By default this is what is displayed. This means you cannot override the colors in this mode. If you want a flat icon, or want to manipulate the color yourself, set the gradient prop to false.

Instagram

Just like Google, Instagram's logo uses gradients. Likewise, you must set gradient to false to change the color.

LinkedIn

Just like Facebook's logo, LinkedIn's logo may appear as a cutout. Likewise, there is a targets props to target the background and in SVG elements. Each target element also has a data-name attribute for targeting with CSS.

TikTok

TikTok as two variants: multicolor and solid.

multicolor is the default. It includes three layers of color: top, middle, and bottom each of which can be targeted individually with the targets prop. Each target element also has a data-name attribute for targeting with CSS.

solid is a single path element, displaying.

𝕏

This is a standard solid, single path element.

YouTube

As you may expect at this point, YouTube has the targets background and play, but the play icon can also be cut out with the cutout prop. Each target element also has a data-name attribute for targeting with CSS.

Hooks

Color Scheme Detection (useColorScheme())

This is the simplest way to get the user's color scheme preference (keep it up to date) to apply JavaScript-based styling. It returns 'light' or 'dark'.

1
2
3
4
5
6
7
8
9
10
11
12
13
import { useColorScheme } from '@andrilla/mado-ui/hooks'

export function MyComponent() {
	const colorScheme = useColorScheme()

	return (
		<div style={
			colorScheme === 'light'
				? { backgroundColor: 'white', color: 'black' }
				: { backgroundColor: 'black', color: 'white' }
		} />
	)
}

Form Status

When you have a really large form, keeping track of every input or group of inputs, their validity, and the form's overall status, can be challenging. It's common to separate inputs into grouped components, and link them with a shared context. This hook makes it easy to setup and keep track of.

This hook exports the following:

useFormStatus()
This provides methods for accessing and managing form status. See more details below.
FormStatusProvider
Wrap this around your Form component to provide form status context to all of its children.
useFormStatus() Details

This hooks provides the following methods:

formStatus
setFormStatus()
checkValidity()

Mobile Device Detection (useMobileDevice())

This is the simplest way to get the user's device type (keep it up to date) to apply provide different touch-specific features (or mouse-specific features). It returns a boolean.

1
2
3
4
5
6
7
8
9
import { useMobileDevice } from '@andrilla/mado-ui/hooks'

export function MyComponent() {
	const isMobile = useMobileDevice()

	return (
		<button>{isMobile ? 'Tap' : 'Click'} Me</button>
	)
}

Pointer Movement Tracking

When triggered, this hook tracks fullscreen pointer movement. Most of the time, you should use createDraggable from animejs. The difference here is that the goal is to track the delta of movement from a start position.

Props

Optionally use these props when initializing the hook.

activeCursor
Sets the cursor when actively tracking pointer movement. (default: 'grabbing')
onChange()
A callback function that is called when the pointer movement changes. Type: (delta: Coords) => void
onEnd()
A callback function that is called when the pointer movement ends. Type: (delta: Coords) => void
Return

To take advantage of the pointer movement tracking, use these returned values:

delta
This is the delta value of the pointer movement from the start position to the current position. It provides an x and y coordinate value that updates as the pointer moves.
stopTracking()
This is a manual way to stop tracking pointer movement. It resets the delta value and stops the tracking process. If you don't call this, pointer movement stops tracking automatically in the scenarios listed in the next section.
trackPointerMovement()
Call this to start tracking pointer movement. You must pass an event object to the function. It only accepts events from mousedown, mouseenter, mouseover, pointerenter, pointerdown, pointerover, and touchstart.
Utilities

Class Management

These utilities make CSS class name management easier, but they just use the native classList API under the hood. Each function accepts an element or elements as the first param, and a className or classList as the second param.

hasClass(element, className)
Returns a boolean based on whether the specified element has the specified class.
addClass(elements, classList)
Adds the specified classes to the specified elements.
removeClass(elements, classList)
Removes the specified classes from the specified elements.
toggleClass(elements, classList)
Toggles the specified classes on the specified elements

Custom Tailwind Merge

Use this custom implementation of twMerge—instead of twMerge from the tailwind-merge library—for merging Tailwind CSS class names or extending twMerge.

twMerge()
Works the same as twMerge from the tailwind-merge library, but includes all of the extensions in 窓 UI library.
extendTailwindMerge()
Works the same as extendTailwindMerge from the tailwind-merge library, but includes all of the extensions in 窓 UI library.

Get Date

Straightforward functions for working with Date objects. This does not completely replace the standard Date object, but it helps with collecting date-related information.

monthNamesList
Provides a chronological list of month names.
weekdayNamesList
Provides a chronological list of weekday names.
currentMonthName
Returns the name of the current month, using getMonthName()
currentWeekdayName
Returns the name of the current weekday, using getWeekdayName()
daysInMonth(date)
Returns the number of days in the month of the given (default: current) date.
firstOfMonth(date)
Returns the first day of the month for the given (default: current) date.
getDate(date)
Works just like Date.getDate(), but pads the result with a leading zero if it is a single digit.
getHours(hours)
Works just like Date.getHours(), but pads the result with a leading zero if it is a single digit.
getHoursIn12(hours)
Works just like Date.getHoursIn12(), but converts to 12-hour format and pads the result with a leading zero if it is a single digit.
getMeridianFromHour(hour)
Returns 'am' or 'pm' based on the given hour.
getMilliseconds(milliseconds)
Works just like Date.getMilliseconds(), but pads the result with a leading zero if it is a single digit.
getMinutes(minutes)
Works just like Date.getMinutes(), but pads the result with a leading zero if it is a single digit.
getMonth(month)
Works just like Date.getMonth(), but adds 1 (to account for zero-based indexing) and pads the result with a leading zero if it is a single digit.
getMonthIndexFromName(name)
Returns the index of the specified month.
getMonthName(month)
Returns the name of the month for the specified date. If providing a number, use the index of the month.
getNextMonth(month)
Returns the index of the following month from the specified month.
getPreviousMonth(month)
Returns the index of the previous month from the specified month.
getSeconds(date)
Works just like Date.getSeconds(), but pads the result with a leading zero if it is a single digit.
getUserReadableDate(date, options)
Returns the simplest date string from the specified date. This includes 'Today', 'Tomorrow', and 'Yesterday'. If not one of those, it utilizes toFullDateString() to return the string. If the date is within the current year, the year is omitted from the string. You may use the same options as toFullDateString() to customize the output.
getWeekdayName(weekday)
Returns the weekday name of the specified day. If you provide a number, use an index of 0-6 to get the name of the corresponding day.
getYearsInRange(minYear, maxYear)
Returns an array of years in between the specified minimum and maximum years, inclusive.
toFullDateString(date, options)
Returns a formatted string to display the date in your chosen format.
getUserReadableDateFromTimestampz(date, options)
Converts a string timestamp to a user-readable date string.

React Helpers

These are just a couple of helper functions that we use in this project.

getTextFromChildren(children)
Returns the text content of the specified React children.
getElementFromMaybeRef(refOrElement, options)
Use this when you could receive either a ref or an element directly. It will return an element or null if the ref is not valid.

RegExp

Helpful regular expressions for parsing and validating strings.

emailRegExp
A regular expression for validating email addresses.
isEmail(email)
Runs a check to see if the given string is a valid email address.
telRegExp
A regular expression for validating telephone numbers.
isPhoneNumber(tel)
Runs a check to see if the given string is a valid telephone number.
wordOrNumberRegExp
A regular expression for capturing words or numbers. This is used for splitting data-formatted strings (camelCase, snake_case, kebab-case, PascalCase, and Title Case) into their component parts, maintaining the original case and digits grouped together.

String Manipulation

splitWords()
This splits data-formatted strings (camelCase, snake_case, kebab-case, PascalCase, and Title Case) into their component parts, maintaining the original case and digits grouped together.
formatPhoneNumber(phoneNumber, options)
Formats any string to a valid phone number format. You may use the options parameter to select a specific phone number format, prepend a specific country code, and fill in missing digits with the character of your choice.
toCamelCase(string)
Converts any string to camel case.
toLowerCase(string)
Converts a string to lowercase, and offers easy string replacements for creating snake case, kebab case, or your own. Use the options [delimiter, joiner] to customize the replacement behavior.
toTitleCase(string)
Converts any string to title case.