Skip to content

dan1229/nextjs-base-blocks

Repository files navigation

NextJS Base Blocks

Lint Build Component Tests codecov

By: Daniel Nazarian 🐧👹


Description

Base Blocks is a collection of UI 'blocks' that are used to build the rest of the components used on the sites.

This is intended to be used as a git submodule in a NextJS + Typescript project.

A block ideally is the smallest unit of UI code that can be reasonably reused. It is a self-contained piece of code that can be used in any context. It is not a component, but a building block for other, bigger components.

The idea behind this is that you can standardize your components and styling without an overabundance of classes and styles or a third party style library like bootstrap or tailwind (you can still switch to one if you want though!) This will make it easier to maintain and update your codebase as requirements grow.

If you ever want to move away or edit the base components, you can easily remove the git submodule and take what you need.

The goal here is to provide consistent and easy to use components that can be used in any project, helping you get a project off the ground quickly or to improve an existing project.

Why A Submodule?

One of the most common questions about this project - it could easily be an NPM package like Tailwind or similar.

While true and this package is meant to serve a similar purpose, the reality is this is just maintained by myself (Daniel Nazarian). So, sadly, this project is far more likely to have bugs or just generally not fit every edge case its end users may be searching for.

To that end, I chose to make it a submodule so client projects could easily directly implement fixes, improvements and even fork it themselves if they choose to go a completely different direction.

While I love contributions to the repo directly I didn't want to force people to do that should they run into a bug or something like that.

Requirements

You must have the following installed to use this submodule

See package.json for all the requirements.

Since this is a submodule it can't enforce these requirements, but you will get errors if you don't have them installed.

Components

At the moment, the following components are available:

  • BBAlert
  • BBButton
  • BBCard
  • BBCollapsible
  • BBLink
  • BBLoadingSpinner
  • BBModal
  • BBNavbar
  • BBNavbarItem
  • BBText

As well as the following 'Form Components':

  • BBFieldBase
  • BBFieldCheckbox
  • BBFieldSelect
  • BBFieldFile
  • BBFieldNumber
  • BBFieldSelectCard
  • BBFieldSelectMultiple
  • BBFieldText

Also including a number of form helper functions and components.

Form Components Sizing: All form components support sm, md, and lg sizing options that maintain proper proportional dimensions. The checkbox sizing system uses SCSS mixins to ensure consistent visual scaling across all size variants.

SCSS Variables

You are also able to customize Base Blocks via SCSS variables. You typically will want to put these in globals.scss.

The list of available options is here:

// keep these variables separate to allow for dynamic lighten/darken usage
// NOTE: these are not required but it is highly recommended to at least
// set these theme colors
$primary-color: #5a65ff;
$secondary-color: #45b689;
$accent-color: #FF6B6C;
$info-color: #FFC800;
$warning-color: #f1c500;
$success-color: #50c758;
$danger-color: #78CDD7;

html,
:root {
  // app theme
  --primary-color: #{$primary-color};
  --primary-dark-color: #{darken($primary-color, 10%)};
  --primary-darkest-color: #{darken($primary-color, 20%)};
  --primary-light-color: #{lighten($primary-color, 10%)};
  --secondary-color: #{$secondary-color};
  --secondary-dark-color: #{darken($secondary-color, 10%)};
  --secondary-darkest-color: #{darken($secondary-color, 20%)};
  --secondary-light-color: #{lighten($secondary-color, 10%)};
  --accent-color: #{$accent-color};
  --accent-dark-color: #{darken($accent-color, 10%)};
  --accent-light-color: #{lighten($accent-color, 10%)};
  --accent-darkest-color: #{darken($accent-color, 20%)};
  // general colors
  --white: #fff;
  --black: rgb(35, 35, 35);
  --grey-lightest: #abb3c0;
  --grey-light: #b1bfd5;
  --grey-dark: #969dac;
  --grey-darkest: #6f7e9d;
  // theme colors
  --info-color: #{$info-color};
  --info-dark-color: #{darken($info-color, 10%)};
  --info-light-color: #{lighten($info-color, 10%)};
  --warning-color: #{$warning-color};
  --warning-dark-color: #{darken($warning-color, 10%)};
  --warning-light-color: #{lighten($warning-color, 10%)};
  --success-color: #{$success-color};
  --success-dark-color: #{darken($success-color, 10%)};
  --success-light-color: #{lighten($success-color, 10%)};
  --danger-color: #{$danger-color};
  --danger-dark-color: #{darken($danger-color, 10%)};
  --danger-light-color: #{lighten($danger-color, 10%)};
  // bg theme colors
  --bg-color: rgb(233, 233, 233);
  --bg-light-color: rgba(241, 241, 241, 1);
  --bg-dark-color: rgb(189, 189, 189);
  // bb alert
  --alert-border-radius: 1rem;
  // bb button
  --button-base-padding: 0.6rem;
  --button-border-radius: 1rem;
  // bb card
  --card-background-default-color: rgb(250, 250, 250);
  --card-border-default-color: rgb(230, 230, 230);
  --card-border-radius: 1rem;
  --card-border-thickness: 2px;
  --card-elevation-default: rgba(0, 0, 0, 0.12) 0px 1px 1px, rgba(0, 0, 0, 0.24) 0px 1px 1px;
  --card-elevation-low: rgba(0, 0, 0, 0.12) 0px 1px 3px, rgba(0, 0, 0, 0.24) 0px 1px 2px;
  --card-elevation-med: rgba(0, 0, 0, 0.12) 0px 3px 6px, rgba(0, 0, 0, 0.24) 0px 3px 6px;
  --card-elevation-high: rgba(0, 0, 0, 0.12) 0px 10px 20px, rgba(0, 0, 0, 0.24) 0px 6px 6px;
  // bb divider
  --divider-color-default: rgb(221, 221, 221);
  // bb loading
  --loading-default-variant: default;
  --loading-default-size: md;
  --loading-default-color: primary;
  --loading-thickness: 2px; 
  // bb modal
  --modal-z-index: 9999;
  // bb navbar
  --navbar-background-color: var(--primary-color);
  --navbar-icon-mobile-color: var(--primary-color);
  --navbar-transition: all 0.2s ease-in-out;
  // responsive vertical navbar widths for different screen sizes
  --navbar-vertical-width-xs: 5rem;
  --navbar-vertical-width-sm: 6rem;
  --navbar-vertical-width-md: 8rem;
  --navbar-vertical-width-lg: 10rem;
  --navbar-vertical-width-xl: 11rem;
  // bb navbar item
  --navbar-item-border-radius: 1rem;
  --navbar-item-padding: 0.5rem;
  --navbar-item-background-color: transparent;
  --navbar-item-text-color: var(--accent-color);
  --navbar-item-text-color-hover: #{darken($accent-color, 20%)};
  --navbar-item-bg-color-active: #{lighten($primary-color, 45%)};
  --navbar-item-bg-color-hover: #{lighten($accent-color, 20%)};
  --navbar-item-border-thickness: 2px;
  --navbar-item-border-default-color: var(--primary-color);
  // bb tooltip
  --tooltip-padding: 0.5rem 1rem;
  --tooltip-border-radius: 8px;
  --tooltip-z-index: 9000;
  --tooltip-box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  --tooltip-arrow-size: 4px;
  // bb form components
  --form-input-bg: var(--bg-color);
  --form-input-border-color: var(--bg-dark-color);
  --form-input-color: var(--text-color-default);
  --form-input-focus-color: var(--secondary-color);
  --form-input-focus-bg: var(--bg-color);
  --form-input-focus-border-color: var(--secondary-color);
  --form-input-placeholder-color: var(--grey-lightest);
  --form-input-disabled-color: var(--grey-lightest);
  --form-input-disabled-bg: var(--bg-color);
  --form-input-disabled-border-color: var(--grey-dark);
  --form-file-button-border-color: var(--grey-dark);
  --form-file-button-hover-color: var(--white);
  --form-file-button-color: var(--text-color-default);
  --form-file-button-hover-bg: var(--primary-color);
  --form-input-plaintext-color: var(--white);
  --form-select-arrow-color: var(--form-input-color);
  --form-required-indicator-color: var(--form-input-color);
  // bb table
  --table-cell-bg: var(--card-background-default-color);
  --table-header-bg: var(--bg-light-color);
  --table-row-hover-bg: var(--bg-color);
  --table-row-selected-bg: var(--accent-color);
  // bb text
  --text-color-default: rgb(24, 24, 24);
  --text-size-xs: 0.5rem;
  --text-size-s: 0.75rem;
  --text-size-m: 1rem;
  --text-size-l: 1.5rem;
  --text-size-xl: 2rem;
  --text-size-xxl: 2.5rem;
  --text-size-xxxl: 3rem;
  --mobile-text-size-xs: 0.6rem;
  --mobile-text-size-s: 0.8rem;
  --mobile-text-size-m: 1rem;
  --mobile-text-size-l: 1.2rem;
  --mobile-text-size-xl: 1.6rem;
  --mobile-text-size-xxl: 2rem;
  --mobile-text-size-xxxl: 2.5rem;
  // font family - page router font usage
  --font-family-main: 'Josefin Sans';  // Used for main text
  --font-family-header: 'Montserrat';  // Used for larger fonts/headers
  --font-family-navbar-header: 'Montserrat';  // Used for navbar headers
  --form-input-font-family: 'Montserrat' // Font family for all form fields
  --form-label-font-family: 'Montserrat' // Font family for all form field labels
  // font family - app router font usage
  --font-family-main: var(--font-lexend);
  --font-family-header: var(--font-lexend);
  --font-family-navbar-header: var(--font-lexend);
  --form-input-font-family: var(--font-family-main); // Font family for all form fields
  --form-label-font-family: var(--font-family-main); // Font family for all form field labels
}

While none of these variables are required, it definitely will help make your app look and feel more custom.

It is recommended to copy and paste this whole block into your globals.scss file and then edit the variables as needed.

Responsive Breakpoints & SCSS Mixins

The responsive mixins provide consistent breakpoints and utilities throughout your application.

Setup (Required - One-time breaking change in v2.2.0)

Add this to your next.config.js:

const { configureSubmoduleSass } = require('./base_blocks/mixins');

const nextConfig = {
  // configure sass for this project and nextjs base blocks
  sassOptions: configureSubmoduleSass(__dirname),
  // ... rest of your config
};

module.exports = nextConfig;

⚠️ Your build will fail without this setup - the error message should guide you here.

Usage

After setup, these mixins are automatically available in all your .scss files to use as so:

Responsive Breakpoints:

@include media-lg {
  ...
}

@include media-sm {
  ...
}

BB components will automatically use these breakpoints to be mobile friendly.

See mixins.scss for the specific breakpoints and mixins available.

Dark Mode / Themes

Add overrides for dark themes like so:

[data-theme='dark'] {
  // override any of the above variables
  --bg-color: rgb(25, 25, 25);
  --bg-light-color: rgb(60, 60, 60);
  --bg-dark-color: rgb(39, 39, 39);
  // bb navbar
  --navbar-background-color: rgb(54, 54, 54);
  // bb card
  --card-background-default-color: rgb(77, 77, 77);
  --card-border-default-color: rgb(77, 77, 77);
  // bb divider
  --divider-color-default: rgb(77, 77, 77);
  // bb text
  --text-color-default: rgb(255, 255, 255);
}

Testing

This project includes comprehensive testing with Cypress component testing and code coverage.

Running Tests

# Run all component tests
npm run test

# Run tests with coverage report
npm run test:coverage

# Open Cypress GUI for component testing
npm run test:open

# Generate coverage reports
npm run coverage:report

# Open HTML coverage report in browser
npm run coverage:open

Test Structure

  • Component Tests: Located in cypress/component/ directory
  • Test Utilities: Custom commands and helpers in cypress/support/
  • Coverage Reports: Generated in coverage/ directory

All base components have comprehensive test coverage including:

  • Rendering tests for all prop variations
  • Interaction testing (clicks, hovers, form inputs)
  • Responsive behavior testing
  • Accessibility testing
  • Edge case handling

Writing New Tests

To create a new test file, use the template in cypress/support/_template.cy.tsx or follow the existing test patterns. All tests should follow the established naming convention: component-name.cy.tsx.

Demo

This project is also a standalone NextJS project that you can run to see the components in action.

To do so, simply run:

npm run dev

Then navigate to http://localhost:3000 to see the demo of the components.

Form Components Demo are available at http://localhost:3000/form-components.

Please note this is a work in progress.


Copyright 2025 © Daniel Nazarian.

About

NextJS + TypeScript component library - meant to be used as a git submodule

Topics

Resources

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •