Skip to content

0kb footprint, modular, configurable SASS framework to be used with CSS modules (or not).

License

Notifications You must be signed in to change notification settings

wintercounter/phrame

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

50 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Phrame2

Modular, configurable SASS framework mostly to be used with CSS modules.

What Phrame2 offers:

  • 0kb footprint
  • Multilevel configuration system
  • Config references
  • Color/Scheme management
  • Exposed breakpoint rules
  • Easing library
  • Helper mixins
  • Modules
  • No bloat
  • Use on existing codebase

Table of contents

Basic setup

npm install phrame2 --save

Now use it:

// Load the environment (has not output)
@import '~phrame2/core';

// Just a simple reset (optional)
@import '~phrame2/module/reset';

// Some basic styles (optional)
@import '~phrame2/module/basic';

// Expose breakpoints (optional)
@import '~phrame2/module/expose';

Phrame2 related stuff (mixins, functions, etc.) are prefixed with an _ so Phrame2 won't conflict your codebase.


Config system

The config system uses Sass Maps as a multi dimensional array - since Sass is only able to handle a single level (looking at you map-get).

With Phrame2 my primary goal was to build a flexible tool to handle multiple sites/schemes/styles/layouts from one codebase. For this to work I needed a system where it's simple to override the common stuff and makes easy to switch color schemes for example. To achieve this I've built a flow where you can overwrite the default configuration.

Flow

To use such behavior you need to import your own instance of Phrame2 into your codebase.

Load Phrame2 Env -> Config Overrides -> Output

Here's a basic structure with the ability to override config values:


├── components
│   └── MyAwsomeCmp
│        └── MyAwsomeCmp.js
│        └── MyAwsomeCmp.scss
│
└── scss
    ├── core.scss
    └── config.scss

With these contents (and flow backwords to the root):

MyAwsomeCmp.js

import styles from './MyAwesomeCmp.scss'

MyAwsomeCmp.scss

// Always import this `core` instead of Phrame2 directly
@import '../../scss/core';

body {
    background-color: _scheme('body.bg');
    font-size: _config('size.large');

    @include _on('mobile') {
        background-color: _scheme('body.bg-on-mobile');
        font-size: _config('size.sub-sizes.small');
    }
}

core.scss

@import '~phrame2/core';
@import './config';

config.scss

// $_ is just a temporary assignment
// because this is how functions work in Sass
$_: _config('size', (
    'small': 3.0em,
    'big': 8.0em,
    'large': 1.4em,
    'sub-sizes': (
        'small': 1.0em,
        'big': 2.0em,
        'large': 3.4em
    )
));

Config Management

In case you're building a multi-(site/color) solution, or just need to make the environment configurable, you may end up having multiple configuration files.

Phrame2 comes with multiple solutions to manage these options/values easily.

@function _config(key[, value])

  • key The key of the config property (size.small)
  • value (optional): any

Good to know: in fact, key is optional too. Returns full config object. Good for debugging purposes.

Good to know: this is just a shorthand function, _config-set and _config-get is available too.

Usage examples

Override config

// Defaults
$_: _config('_namespace', (
    'option-1': 'original-value-1',
    'option-2': 'original-value-2',
    'option-3': 'original-value-3'
));

// Overwrites
$_: _config('_namespace', (
    'option-1': 'new-value-1',
    'option-new-1': 'new-value-2'
));

// Results
$_: _config('_namespace', (
    'option-1': 'new-value-1',
    'option-2': 'original-value-2',
    'option-3': 'original-value-3',
    'option-new-1': 'new-value-2'
));

Merge multiple levels

// Defaults
$_: _config('_namespace', (
    'option-1': (
        'inner-option-1': 12px
    ),
    'option-2': 'original-value-2'
));

// Overwrites
$_: _config('_namespace', (
    'option-1': (
        'new-inner-option': 'value'
    ),
    'option-2': 'original-value-2'
));

// Results
$_: _config('_namespace', (
    'option-1': (
        'inner-option-1': 12px,
        'new-inner-option': 'value'
    ),
    'option-2': 'original-value-2'
));

Simplified write

// Defaults
$_: _config('_namespace', (
    'option-1': (
        'inner-option': 12px
    ),
    'option-2': 'original-value-2'
));

// Set new value
$_: _config('_namespace.option-1.inner-option', 20px);

// Results
$_: _config('_namespace', (
    'option-1': (
        'inner-option': 20px
    ),
    'option-2': 'original-value-2'
));

Built-in Tools

There are some built-in utilities based on the config management system. All of them have shorthands, but under the hood the're using _config() with a namespace like _scheme.

These tools are made to manage the most common aspects:

  • path: asset urls
  • scheme: colors
  • screen: responsiveness
  • typography: sizes, families

@function _path($key[, $filename])

  • $key The key of the path
  • $filename (optional) Name of the file
// Defaults from Phrame2
$_: _config('_path', (
    'backgrounds': '../backgrounds/',
));

// Extend with your own
$_: _path('_path', (
    'medals': (
        'direct-bg': '../images/medals/bg.png',
        'bronze': '../images/medals/bronze/',
        'silver': '../images/medals/silver/',
        'gold': '../images/medals/gold/'
    )
));

// Use it
body {
    background-image: url(_path('backgrounds', 'blue.jpg'));
}

.medals{
    background-image: url(_path('medals.direct-bg'));
}

.medal {
    &-gold {
        background-image: url(_path('medals.gold', 'small.jpg'));
    }
    ...
}

@function _scheme($key[, $value])

  • $key Scheme key path
  • $value (optional) Color library reference or unit value

This is a tricky stuff, because built-in color management splits into 2 part:

  1. Color library
  2. Scheme

The Color library is responsibe to store the colors:

$_color-library: (
    'transparent': transparent,
    'white': (
        'main': #ffffff
    ),
    'black': (
        'main': #000000
    ),
    'red': (
        'main': #ff0000
    ),
    'yellow': (
        'main': #ffcc00
    ),
    'green': (
        'main': #00ff00
    ),
    'social': (
        'facebook': #3B5998,
        'twitter': #55ACEE,
        'instagram': #3F729B,
        'google-plus': #DD4B39,
        'tumblr': #36465D
    ),
    'gradient': (
        'white-to-black': (#ffffff #000000)
    )
);

Then use Scheme to define the colors for your elements.

$_scheme-config: (

    // Commons
    common: (
        alert: 'yellow.main',
        success: 'green.main',
        error: 'red.main',
        disabled: 'gray.main'
    ),

    // Typography
    text: (
        base: 'black.main',
        link: 'yellow.main',
        link-hover: 'black.main'
    )
);

The use it in your stylesheet:

.alert {
    background-color: _scheme('common.alert');
}

What's the problem with using 1 level, or just variables?

Sure you can do the following in case you don't need such complexity.

$_: _config('colors', (
    'main': #000,
    'secondary': #fff
));

body {
    color: _config('colors.main');
}

At a certian point you'll end up having the same color values repeated dozens of times. Keeping the available color palette in the Color Library solves this issue, helps you prevent duplicates and also provides a proper name (key) to get the correct color.

@function _color-library($key[, $value])

  • $key Key of the value
  • $value (optional) Value itself

Shorthand function to get/set color-library related values.

@function _screen($key[, $value])

  • $key Key of the value
  • $value (optional) Value itself

Shorthand function to get/set screen related values.

These values will be used by the built-in _on mixin.

// Defaults
$_screen-config: (
    size: (
        mobile: (
            from: 0,
            to: 700
        ),
        tablet: (
            from: 701,
            to: 1024,
        ),
        small: (
            from: 1025,
            to: 1440
        ),
        medium: (
            from: 1441,
            to: 1920,
        ),
        large: (
            from: 1921,
            to: 3000
        )
    )
);

// Add a new screen size
$_: _screen('size.myDevice', (
    from: 0,
    to: 3000
));

// Or modify
$_: _screen('size', (
    mobile: (
        from: 0,
        to: 625
    )
));

@function _typo($key[, $value])

  • $key Key of the value
  • $value (optional) Value itself

Shorthand function to get/set typography related values.

These values are used by the basic module and stores your typography related values.

// Defaults
$_typo-config: (
    font-size: (
        root: 62.5%, // Should not be changed, used for em calculations.
        base: 14
    ),
    line-height: (
        root: 1.5
    ),
    font-family: (
        sans: unquote('Arial, Helvetica, sans-serif'),
        serif: unquote('"Times New Roman", Times, serif'),
        secondary: unquote('"Courier New", Courier, monospace')
    )
);

pre, code {
    font-family: _typo('font-family.secondary');
}

These values will be used by the basic module, but it's optional.

See basic module for more details.

@function _em($target, $base)

Function used for em value calculations.

div {
    font-size: 1.4em; // 14px
    
    span {
        font-size: 1em; // 14px
    }
    
    strong {
       font-size: _em(20, 14); // 1.42857em === 20px 
    }
}

Please not that for this to work correctly, you need to use 62.5% as your root font-size. See basic module details about this.

@function _easing($key[, $value])

  • $key Key of the easing
  • $value (optional) Easing value

Shorthand function to get/set easing related values.

// Usage

.has-transition {
    transition: width 1s _easing('ease-in-out-expo');
}

.has-animation {
    animation-timing-function: _easing('ease-in-out-expo');
}

Referencing keys

Sometimes you might want to say like: "I want this text to have the same color always as the title." Well you can do that using an @ sign.

When using references, you have to use full path.

$_: _config('subpage-scheme', (
    'title': 'red.dark',
    'this-text': '@subpage-scheme.title',
    'other-text': '@whoknows.foo.bar'
));

This also helps to prevent to end up with such situations:

.not-app-title {
    color: _scheme('app.background'); // Defined with the same color I needed
}

Mixins

@mixin _triangle($size, $color, $direction)

Creates a CSS triangle. Based on Zurb Foundation's solution.

  • $size Size of triangle (px, em, etc.)
  • $color Hmm... Color of the triangle I guess. Use a color directly, or a scheme key.
  • $direction top, right, bottom or left
.tooltip {
    &::before {
        @include _triangle(
            12px,
            'tooltip.bg', // or simply #000
            bottom
        );
    }
}

@mixin _scrollbar([$overrides])

Styles scrollbars where it's possible through CSS. Supports IE10+ and Chrome. Rest of the browsers doesn't allow, but hey, it's still a better solution than the shitty JS scrollbars!

  • $overrides (optional) Override default values (coming from Scheme)
// Usage
body {
    @include _scrollbar;
}

// For just one div
.div {
    @include _scrollbar;
}

// Override default config
$_: _scheme('scrollbar', (
    'thumb-background': 'grey.main'
));

// Direct value overwrite
.div {
    @include _scrollbar((
        'thumb-background': 'grey.main'
    ));
}

@mixin _on($breakpoint[, $up-down, $orientation])

Handles responsive rules

  • $breakpoint Breakpoint name defined in _screen.scss, or from value, or retina.
  • $up-down (optional) The given range and up/down, or to value.
  • $orientation (optional) portrait, landscape
// Usages
.title {
    font-size: 1.3em;
    
    // 0-700px
    @include _on(mobile) {
        font-size: 1em;
    }
    
    // 1024px and below
    @include _on(tablet, down) {
        font-size: 1em;
    }
    
    // 701px and up
    @include _on(tablet, up) {
        font-size: 1em;
    }
    
    // 1025px-1440px and portrait
    @include _on(small, false, portrait) {
        font-size: 1em;
    }
    
    // Only landscape
    @include _on(false, false, landscape) {
        font-size: 1em;
    }
    
    // Only on hdpi devices
    @include _on(retina) {
        font-size: 1em;
    }
}

Modules

Modules are optional. Modules have output! It's a good practice to load these modules at boot level.

// Common usage and flow
@import '~phrame2/core';
@import 'myConfigOverrides';
@import '~phrame2/module/reset';
@import '~phrame2/module/basic';
@import '~phrame2/module/expose';
@import 'myOwnStuff';

basic

Includes some basic styles for your page. Here is the full code of the module, I'll walk through it now.

html {
	// Root font size. 62.5% === 1em === 10px
    font-size: _typo('font-size.root');
    // We want the same size everywhere
    -webkit-text-size-adjust: 100%;
}

// Base font
body {
    font-family: _typo('font-family.sans');
    font-weight: normal;
    font-style: normal;
    line-height: _typo('line-height.root');
}

// Bbox sizing as it should be
*, *:after, *:before {
    box-sizing: border-box;
}

reset

A simple reset. You might not need this in case you already have your site built, or using some sort of framework, usually all framework are shipped with reset styles.

expose

This feature was usefull many times for us. It exposes your screen configuration (breakpoints) as JSON in your CSS on body::before. This will let you to have the same rules in JavaScript also, the rules are have to be maintained only from one place.

// Normalizes JSON
let normalize = s => s.replace(/^['"]+|\s+|\\|(;\s?})+|['"]$/g, '')

let breakpoints = JSON.parse(
    normalize(
        getComputedStyle(document.body, ':before').getPropertyValue('content')
    )
)

Q&A

Why Phrame2, where is the first one?

Phrame1 is still alive. It's almost the same, except the bloat. It's like those powerful frameworks out there seeing every day, and even more. Using the same configuration system, it is generating modules based on configurations like grid, form elements and such.

Then started to use CSS modules... I wanted to have a system I can import into the Scss explicitly without any footprint. So basically Phrame2 is the shell of the first one.

Oh yeah, I've almost forgot. The name is also taken on npm :)

Why no more modules (grid, form elements, etc.) and other shiny mixins?

Because Phrame2 is more like a shell than a framework - unlike the first version. Also there is no grid module because when I started to use CSS modules with flexbox, I've realized it's easier to just write my needs on my own.

float and inline-block based grids are simple, it's easy to write a few classes, a generator around it. This is mostly because they all follow this rule: ( 12 || 16 || 24 ) + ( px || em || % )

But flexbox is flexible (oO). Every flexbox based grids out there are totally different. Simply because it is not designed for grids. CSS grids are for grids, but until we have that in every modern browser, I'm fully satisfied with the bloat-free stylesheet and bloat-free markup I get. It looks ugly in React components as well anyway.

However, since modules are optional, maybe I'll consider to have more modules included (and/or port some from 1), everyone can decide which one to use for his project.

Final words

As you can see, Phrame2 is not a full featured framework. It's a higher order shell instead to kick start your own framework, it helps to build your own system for your own custom needs.

About

0kb footprint, modular, configurable SASS framework to be used with CSS modules (or not).

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages