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>