By: Daniel Nazarian 🐧👹
Contact me at dnaz@danielnazarian.com
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.
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.
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.
At the moment, the following components are available:
BBAlertBBButtonBBCardBBCollapsibleBBLinkBBLoadingSpinnerBBModalBBNavbarBBNavbarItemBBText
As well as the following 'Form Components':
BBFieldBaseBBFieldCheckboxBBFieldSelectBBFieldFileBBFieldNumberBBFieldSelectCardBBFieldSelectMultipleBBFieldText
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.
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.
The responsive mixins provide consistent breakpoints and utilities throughout your application.
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.
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.
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);
}This project includes comprehensive testing with Cypress component testing and code coverage.
# 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- 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
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.
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 devThen 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.