OpenValue
Rachèl Heimbach
Principal Consultant @ OpenValue
Solution Architect @ Rabobank
A front-of-the-front-end developer is a web developer who specializes in writing HTML, CSS, and presentational JavaScript code.
A back-of-the-front-end developer is a web developer who specializes in writing JavaScript code necessary to make a web application function properly.
Breaking down UI into fundamental building blocks.
https://atomicdesign.bradfrost.com/chapter-2/
.button {
background-color: #2563eb;
}
:root {
--color-blue: #2563eb;
}
button {
background-color: var(--color-blue);
}
:root {
--button-bg-color: #2563eb;
}
button {
background-color: var(--button-bg-color);
}
:root {
// ...
--color-primary-500: #3b82f6;
--color-primary-600: #2563eb;
--color-primary-700: #1d4ed8;
// ...
--button-bg-color: var(--color-primary-600);
}
button {
background-color: var(--button-bg-color);
}
:root {
// ...
--color-primary-500: #3b82f6;
--color-primary-600: #2563eb;
--color-primary-700: #1d4ed8;
// ...
--color-secondary-500: #a855f7;
--color-secondary-600: #9333ea;
--color-secondary-700: #7e22ce;
// ...
--button-primary-bg-color: var(--color-primary-600);
--button-secondary-bg-color: var(--color-secondary-600);
// ...
}
button.primary {
background-color: var(--button-primary-bg-color);
}
button.secondary {
background-color: var(--button-secondary-bg-color);
}
:root {
/* ... */
--button-primary-bg-color: var(--color-primary-600);
--button-secondary-bg-color: var(--color-secondary-600);
}
button.primary {
background-color: var(--button-primary-bg-color);
}
button.secondary {
background-color: var(--button-secondary-bg-color);
}
/* Dark Theme */
[data-theme='dark'] {
--button-primary-background: var(--color-primary-500);
--button-secondary-background: var(--color-secondary-500);
/* ... */
}
:root {
/* Core spacing variables */
--space-xxs: 4px; /* 0.25 × base */
--space-xs: 8px; /* 0.5 × base */
--space-sm: 12px; /* 0.75 × base */
--space-md: 16px; /* 1 × base */
--space-lg: 24px; /* 1.5 × base */
--space-xl: 32px; /* 2 × base */
--space-xxl: 48px; /* 3 × base */
/* ... */
--button-padding: var(--space-xs) var(--space-md);
}
:root {
/* Font Sizes */
--font-xs: 0.75rem; /* 12px */
--font-sm: 0.875rem; /* 14px */
--font-base: 1rem; /* 16px */
--font-lg: 1.125rem; /* 18px */
--font-xl: 1.25rem; /* 20px */
--font-2xl: 1.5rem; /* 24px */
--font-3xl: 1.875rem; /* 30px */
--font-4xl: 2.25rem; /* 36px */
--font-5xl: 3rem; /* 48px */
/* Font Weights */
--font-thin: 100;
--font-extralight: 200;
--font-light: 300;
--font-normal: 400;
--font-medium: 500;
--font-semibold: 600;
--font-bold: 700;
--font-extrabold: 800;
}
https://atomicdesign.bradfrost.com/chapter-2/
https://atomicdesign.bradfrost.com/chapter-2/
https://atomicdesign.bradfrost.com/chapter-2/
https://atomicdesign.bradfrost.com/chapter-2/
https://atomicdesign.bradfrost.com/chapter-2/
https://atomicdesign.bradfrost.com/chapter-2/
https://atomicdesign.bradfrost.com/chapter-2/
https://atomicdesign.bradfrost.com/chapter-2/
Creating reusable and efficient UI components.
"Build components without starting a complex dev setup, force certain data into your database, or navigate around your application"
Design tokens + UI components library
Portrait
landscape
Primary
Max
Min
Max
Min
Max
Min
Primary
Primary
Primary
Max
Min
Max
Min
<section class="product">
<ov-thumbnail></ov-thumbnail>
<div class="info">
<ov-sub-title></ov-sub-title>
<ov-title></ov-title>
<div class="details">
<div>
<ov-rating></ov-rating>
<ov-price></ov-price>
</div>
</div>
</div>
</section>
<section class="product">
<ov-thumbnail></ov-thumbnail>
<div class="info">
<ov-sub-title></ov-sub-title>
<ov-title></ov-title>
<div class="details">
<div>
<ov-rating></ov-rating>
<ov-price></ov-price>
</div>
<div>
<ov-quantity-picker>
</ov-quantity-picker>
</div>
</div>
<div class="footer">
<ov-message type="ok"></ov-message>
</div>
</div>
</section>
<section class="product">
<ov-thumbnail></ov-thumbnail>
<div class="info">
<ov-sub-title></ov-sub-title>
<ov-title></ov-title>
<div class="details">
<div>
<ov-rating></ov-rating>
<ov-price></ov-price>
</div>
<div>
<ov-quantity-picker>
</ov-quantity-picker>
</div>
</div>
<div class="footer">
<ov-message type="ok"
*ngIf="!isLimited; else limited">
</ov-message>
<ng-template #limited>
<ov-message type="warn"></ov-message>
</ng-template>
</div>
</div>
</section>
<section class="product">
<ov-thumbnail></ov-thumbnail>
<div class="info">
<ov-sub-title></ov-sub-title>
<ov-title></ov-title>
<div class="details">
<div>
<ov-rating></ov-rating>
<ov-price></ov-price>
</div>
<div>
<ov-quantity-picker
*ngIf="!isOutOfStock; else outOfStock">
</ov-quantity-picker>
<ng-template #outOfStock>
<ov-button>
<ov-icon></ov-icon>
</ov-button>
</ng-template>
</div>
</div>
<div class="footer">
<ov-message type="ok"
*ngIf="!isLimited">
</ov-message>
<ov-message type="warn"
*ngIf="isLimited">
</ov-message>
<ov-message type="error"
*ngIf="isOutOfStock">
</ov-message>
</div>
</div>
</section>
<section class="product" *ngIf="!isReplaced">
<ov-thumbnail></ov-thumbnail>
<div class="info">
<ov-sub-title></ov-sub-title>
<ov-title></ov-title>
<div class="details">
<div>
<ov-rating></ov-rating>
<ov-price></ov-price>
</div>
<div>
<ov-quantity-picker
*ngIf="!isOutOfStock; else outOfStock">
</ov-quantity-picker>
<ng-template #outOfStock>
<ov-button></ov-button>
</ng-template>
</div>
</div>
<div class="footer">
<ov-message type="ok"
*ngIf="!isLimited">
</ov-message>
<ov-message type="warn"
*ngIf="isLimited">
</ov-message>
<ov-message type="error"
*ngIf="isOutOfStock">
</ov-message>
</div>
</div>
</section>
<section *ngIf="isReplaced">
...
</section>
@Component({ ... })
export class ProductComponent {
@Input() product!: Product;
}
@Component({ ... })
export class ProductComponent {
@Input() product!: Product;
@Input() isLimited = false;
}
@Component({ ... })
export class ProductComponent {
@Input() product!: Product;
@Input() isLimited = false;
@Input() isOutOfStock = false;
}
@Component({ ... })
export class ProductComponent {
@Input() product!: Product;
@Input() isLimited = false;
@Input() isOutOfStock = false;
@Input() isReplaced? = false;
@Input() replacement?: Product;
}
@Component({ ... })
export class ProductComponent {
@Input() product!: Product;
@Input() isLimited = false;
@Input() isOutOfStock = false;
@Input() isReplaced? = false;
@Input() replacement?: Product;
@Output() addToCart = new EventEmitter<number>();
@Output() contactUs = new EventEmitter<Product>();
}
Slots