frontend-classic
SKILL.md
Classic Frontend Development Patterns
CSS/SASS Organization
File Structure (7-1 Pattern)
styles/
├── abstracts/
│ ├── _variables.scss # Colors, fonts, breakpoints
│ ├── _mixins.scss # Reusable mixins
│ └── _functions.scss # SASS functions
├── base/
│ ├── _reset.scss # CSS reset/normalize
│ ├── _typography.scss # Base typography
│ └── _utilities.scss # Utility classes
├── components/
│ ├── _buttons.scss # Button styles
│ ├── _forms.scss # Form elements
│ └── _cards.scss # Card components
├── layout/
│ ├── _header.scss # Header layout
│ ├── _footer.scss # Footer layout
│ ├── _grid.scss # Grid system
│ └── _navigation.scss # Navigation
├── pages/
│ ├── _home.scss # Home page specific
│ └── _contact.scss # Contact page specific
├── themes/
│ └── _default.scss # Default theme
├── vendors/
│ └── _bootstrap.scss # Third-party overrides
└── main.scss # Main import file
Main SCSS Import Order
// main.scss
@import 'abstracts/variables';
@import 'abstracts/mixins';
@import 'abstracts/functions';
@import 'vendors/bootstrap';
@import 'base/reset';
@import 'base/typography';
@import 'layout/grid';
@import 'layout/header';
@import 'layout/footer';
@import 'layout/navigation';
@import 'components/buttons';
@import 'components/forms';
@import 'components/cards';
@import 'pages/home';
@import 'pages/contact';
@import 'themes/default';
@import 'base/utilities'; // Last for override capability
SASS Variables and Mixins
Color System
// _variables.scss
$color-primary: #0066cc;
$color-primary-dark: darken($color-primary, 10%);
$color-primary-light: lighten($color-primary, 10%);
$color-secondary: #6c757d;
$color-success: #28a745;
$color-warning: #ffc107;
$color-danger: #dc3545;
$color-text: #333333;
$color-text-muted: #6c757d;
$color-background: #ffffff;
$color-border: #dee2e6;
// Semantic aliases
$color-link: $color-primary;
$color-link-hover: $color-primary-dark;
Typography Variables
// _variables.scss
$font-family-base: 'Open Sans', -apple-system, BlinkMacSystemFont, sans-serif;
$font-family-heading: 'Montserrat', $font-family-base;
$font-size-base: 16px;
$font-size-sm: 14px;
$font-size-lg: 18px;
$font-weight-normal: 400;
$font-weight-medium: 500;
$font-weight-bold: 700;
$line-height-base: 1.5;
$line-height-heading: 1.2;
Responsive Breakpoints
// _variables.scss
$breakpoint-xs: 0;
$breakpoint-sm: 576px;
$breakpoint-md: 768px;
$breakpoint-lg: 992px;
$breakpoint-xl: 1200px;
$breakpoint-xxl: 1400px;
$breakpoints: (
'xs': $breakpoint-xs,
'sm': $breakpoint-sm,
'md': $breakpoint-md,
'lg': $breakpoint-lg,
'xl': $breakpoint-xl,
'xxl': $breakpoint-xxl
);
Essential Mixins
// _mixins.scss
// Responsive breakpoint mixin
@mixin respond-to($breakpoint) {
@if map-has-key($breakpoints, $breakpoint) {
@media (min-width: map-get($breakpoints, $breakpoint)) {
@content;
}
} @else {
@warn "Unknown breakpoint: #{$breakpoint}";
}
}
// Flexbox center
@mixin flex-center {
display: flex;
justify-content: center;
align-items: center;
}
// Clearfix
@mixin clearfix {
&::after {
content: '';
display: table;
clear: both;
}
}
// Truncate text with ellipsis
@mixin text-truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
// Visually hidden (accessible)
@mixin visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
// Button reset
@mixin button-reset {
background: none;
border: none;
padding: 0;
cursor: pointer;
font: inherit;
color: inherit;
}
Using Mixins
// Example component
.hero {
padding: 2rem;
@include respond-to('md') {
padding: 4rem;
}
@include respond-to('lg') {
padding: 6rem;
}
&__content {
@include flex-center;
flex-direction: column;
}
&__title {
@include text-truncate;
max-width: 100%;
}
}
BEM Naming Convention
Block, Element, Modifier
// Block
.card { }
// Element (double underscore)
.card__header { }
.card__body { }
.card__footer { }
.card__title { }
.card__image { }
// Modifier (double hyphen)
.card--featured { }
.card--compact { }
.card__title--large { }
BEM Example
.navigation {
display: flex;
background: $color-background;
&__list {
display: flex;
list-style: none;
margin: 0;
padding: 0;
}
&__item {
margin: 0 1rem;
}
&__link {
color: $color-text;
text-decoration: none;
&:hover {
color: $color-primary;
}
&--active {
color: $color-primary;
font-weight: $font-weight-bold;
}
}
// Modifier for dark theme
&--dark {
background: #333;
.navigation__link {
color: #fff;
}
}
}
JavaScript Patterns
Namespace Pattern
// Avoid global pollution
var MYAPP = MYAPP || {};
MYAPP.navigation = {
init: function() {
this.bindEvents();
},
bindEvents: function() {
$('.navigation__toggle').on('click', this.toggle.bind(this));
},
toggle: function(e) {
e.preventDefault();
$('.navigation__menu').toggleClass('is-open');
}
};
// Initialize
$(document).ready(function() {
MYAPP.navigation.init();
});
Module Pattern
var MYAPP = MYAPP || {};
MYAPP.modal = (function($) {
// Private variables
var $modal = null;
var isOpen = false;
// Private functions
function bindEvents() {
$(document).on('click', '[data-modal-open]', open);
$(document).on('click', '[data-modal-close]', close);
$(document).on('click', '.modal__overlay', close);
$(document).on('keydown', handleKeydown);
}
function handleKeydown(e) {
if (e.key === 'Escape' && isOpen) {
close();
}
}
// Public functions
function init() {
bindEvents();
}
function open(e) {
e.preventDefault();
var target = $(this).data('modal-open');
$modal = $('#' + target);
$modal.addClass('is-visible');
isOpen = true;
$('body').addClass('modal-open');
}
function close() {
if ($modal) {
$modal.removeClass('is-visible');
isOpen = false;
$('body').removeClass('modal-open');
}
}
// Expose public API
return {
init: init,
open: open,
close: close
};
})(jQuery);
Document Ready Pattern
// Short syntax
$(function() {
// DOM ready
MYAPP.init();
});
// Full syntax (preferred for clarity)
$(document).ready(function() {
MYAPP.init();
});
// When also waiting for images
$(window).on('load', function() {
// All assets loaded
MYAPP.initAfterLoad();
});
jQuery Best Practices
Cache Selectors
// BAD - queries DOM multiple times
$('.header').addClass('sticky');
$('.header').find('.nav').show();
$('.header').attr('data-visible', 'true');
// GOOD - cache the selector
var $header = $('.header');
$header.addClass('sticky');
$header.find('.nav').show();
$header.attr('data-visible', 'true');
// BETTER - chain methods
$('.header')
.addClass('sticky')
.find('.nav').show()
.end()
.attr('data-visible', 'true');
Event Delegation
// BAD - binds to each element (memory heavy)
$('.item').on('click', function() {
$(this).toggleClass('active');
});
// GOOD - delegate from parent (handles dynamic elements)
$('.item-list').on('click', '.item', function() {
$(this).toggleClass('active');
});
// For document-level delegation
$(document).on('click', '[data-action="delete"]', function(e) {
e.preventDefault();
var id = $(this).data('id');
deleteItem(id);
});
AJAX Patterns
// Basic GET request
$.get('/api/items', function(data) {
renderItems(data);
});
// POST with data
$.post('/api/items', { name: 'New Item' }, function(response) {
showSuccess('Item created');
});
// Full AJAX with error handling
$.ajax({
url: '/api/items',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ name: 'New Item' }),
beforeSend: function() {
showLoader();
},
success: function(response) {
showSuccess('Item created');
refreshList();
},
error: function(xhr, status, error) {
showError('Failed to create item: ' + error);
},
complete: function() {
hideLoader();
}
});
Form Handling
// Form submission with AJAX
$('#contact-form').on('submit', function(e) {
e.preventDefault();
var $form = $(this);
var $submit = $form.find('[type="submit"]');
var formData = $form.serialize();
// Disable button during submission
$submit.prop('disabled', true).text('Sending...');
$.ajax({
url: $form.attr('action'),
type: $form.attr('method') || 'POST',
data: formData,
success: function(response) {
$form[0].reset();
showSuccess('Message sent successfully');
},
error: function(xhr) {
var errors = xhr.responseJSON;
showErrors(errors);
},
complete: function() {
$submit.prop('disabled', false).text('Send');
}
});
});
// Client-side validation
function validateForm($form) {
var isValid = true;
$form.find('[required]').each(function() {
var $field = $(this);
var value = $field.val().trim();
if (!value) {
$field.addClass('is-invalid');
isValid = false;
} else {
$field.removeClass('is-invalid');
}
});
return isValid;
}
Animation and Transitions
// Fade animations
$('.notification').fadeIn(300);
$('.notification').fadeOut(300, function() {
$(this).remove();
});
// Slide animations
$('.accordion__content').slideToggle(200);
// Custom animation
$('.element').animate({
opacity: 0.5,
marginLeft: '20px'
}, 300);
// Using CSS classes (preferred for performance)
$('.element').addClass('is-visible');
// Combined with CSS transition
// .element { transition: opacity 0.3s ease; }
// .element.is-visible { opacity: 1; }
Responsive Patterns
Mobile-First CSS
// Start with mobile styles
.container {
padding: 1rem;
// Tablet and up
@include respond-to('md') {
padding: 2rem;
}
// Desktop and up
@include respond-to('lg') {
padding: 3rem;
max-width: 1200px;
margin: 0 auto;
}
}
Responsive Images
.responsive-image {
max-width: 100%;
height: auto;
}
// Background image responsive
.hero {
background-image: url('/images/hero-mobile.jpg');
background-size: cover;
background-position: center;
@include respond-to('md') {
background-image: url('/images/hero-tablet.jpg');
}
@include respond-to('lg') {
background-image: url('/images/hero-desktop.jpg');
}
}
Print Styles
@media print {
// Hide non-essential elements
.navigation,
.footer,
.sidebar,
.btn {
display: none !important;
}
// Ensure content is readable
body {
font-size: 12pt;
line-height: 1.4;
color: #000;
background: #fff;
}
// Show URLs for links
a[href]::after {
content: ' (' attr(href) ')';
}
// Avoid page breaks inside elements
h1, h2, h3, h4 {
page-break-after: avoid;
}
img, table, figure {
page-break-inside: avoid;
}
}
Asset Management
Script Loading Order
<!-- In head: Critical CSS only -->
<link rel="stylesheet" href="/css/critical.css">
<!-- Defer non-critical CSS -->
<link rel="preload" href="/css/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<!-- Before closing body: Scripts -->
<script src="/js/vendor/jquery.min.js"></script>
<script src="/js/vendor/plugins.js"></script>
<script src="/js/main.js"></script>
Cache Busting
<!-- Version query string -->
<link rel="stylesheet" href="/css/main.css?v=1.2.3">
<script src="/js/main.js?v=1.2.3"></script>
<!-- Or hash-based (build tool generated) -->
<link rel="stylesheet" href="/css/main.a1b2c3d4.css">
<script src="/js/main.e5f6g7h8.js"></script>
Weekly Installs
3
Repository
twofoldtech-dak…ketplaceGitHub Stars
1
First Seen
Mar 1, 2026
Security Audits
Installed on
opencode3
gemini-cli3
github-copilot3
codex3
amp3
cline3