Skip to main content

Swiftlink

Updated Mar 22, 2019 ·

HTML5 CSS3 JavaScript Bootstrap Netlify Mailchimp

A responsive landing page for a fictional transportation and logistics company, built with HTML, CSS, JavaScript, and Bootstrap. Deployed on Netlify.

Screenshot

Technologies Used

  • HTML, CSS, JavaScript
  • Bootstrap Modals, Grid, OffCanvas
  • Netlify for deployment
  • Mailchimp for newsletter/contact integration

Key Features

A brief overview of the design elements and user-facing features.

  • Modern, responsive layout
    • Adjusts cleanly from 320px up to large desktops
    • Uses media queries and Bootstrap breakpoints
  • Smooth scrolling and interactive UI
    • CTA sections to guide users
    • Service highlights and animations
  • Contact form
    • Collects basic customer inquiries
    • Integrated with Mailchimp

The navigation bar provides quick access to all major sections of the website. It adapts to different screen sizes and includes both desktop and mobile-friendly elements.

  • Sticky top navigation

    • Remains fixed at the top of the screen during scroll
    • Improves accessibility to key sections at all times
  • Logo on the left

    • Displays company branding
    • Clickable to redirect users to the homepage
  • Main menu (center for desktop, offcanvas for mobile)

    • About, Shipping Services, Logistics Solutions, Business Solutions, Support
    • Dropdowns used for multi-option categories
  • Right section links

    • Search, Global settings, and Login
    • Icons accompanied by text labels for clarity
  • Responsive design with toggler

    • Offcanvas menu triggered for smaller screens
    • Same links and dropdowns are adapted for mobile view
Screen WidthNavbar Behavior
≥1321pxAll navbar items visible
1130px – 1320pxRight-aligned labels hidden; icons only
401px – 1129pxLeft-aligned items hidden; toggler shown
≤400pxOnly logo and toggler are shown

Additional Notes

The Navbar is built with Bootstrap's responsive utilities. It uses classes like navbar-expand-lg for expanding on larger screens and offcanvas for mobile. Dropdowns are used for menu items with multiple sub-options. Each link includes descriptive text and supporting icons to enhance usability.

OffCanvas Menu

OffCanvas Sidebar

offCanvas is used in this project to style the collapsible navbar. At 1129px and below, toggler opens a sidebar with vertically stacked nav items:

  • About
  • Shipping Services (dropdown)
  • Logistics Solutions (dropdown)
  • Business Solutions (dropdown)
  • Support (dropdown)

OffCanvas Width by Screen Size

Screen WidthOffCanvas Width
768px – 1129px50% of screen
400px – 767px70% of screen

Offcanvas Sidebar Behavior

This section describes the interactive behavior of the Bootstrap 5.3 offcanvas sidebar when used on screens 1129px wide and below.

  • Toggler icon opens the sidebar and dims the background
  • Close button hides the sidebar and removes the dim
  • Clicking outside the sidebar closes it and removes the dim
  • Clicking non-dropdown links inside the sidebar:
    • Closes the sidebar
    • Navigates to the section
    • Removes the dim
  • Clicking a dropdown link inside the sidebar:
    • Expands the dropdown
    • Shows the child items
    • Keeps the sidebar open
  • Clicking a dropdown child item:
    • Closes the sidebar
    • Navigates to the section
    • Removes the dim

Search Modal

Details on how the Search modal behaves in various user actions.

  • Modal closes when:

    • User clicks the close button
    • User clicks outside modal
    • User presses "Search"
    • User types a query and presses Enter
  • Modal stays open if:

    • User presses Enter with an empty Search field

Login/Signup Modals

This script handles the opening, closing, panel-switching, and form validation behavior for the Login and Signup modal.

Opening Modals

  • Open and Close Modal

    • Clicking the login icon opens the modal and defaults to the Login panel.
    • Clicking the close button or clicking outside the modal closes it.
    • When the modal closes, all input fields in both panels are cleared.
  • Switch Between Panels

    • Users can toggle between Login and Signup without restriction.
    • Switching panels also resets validation states and form fields.

Form Reset on Close

Closing the modal:

  • Clears all input values.
  • Resets validation state and active panel to Login.

Login Validation

Pressing Enter or clicking Continue checks that the Username field is not empty.

  • If empty:
    • Native browser validation is triggered.
    • Default form submission is blocked.

Validation uses HTML5 form validation (required attribute) to show browser-native warning like:

  • "Please fill out this field"

Signup Validation

info

This is still a work in progress.

Sequential field validation: Prevents submitting the form (via Enter or "Sign Up" button) unless the following fields are all filled:

  • Full Name
  • Email Address
  • Username
  • Password

The first empty required field triggers the browser's built-in validation message.

  • Uses checkValidity() on the form
  • Ensure native handling of "Please fill out this field".

The banner is a full-width hero section that creates an engaging first impression using a background video and a prominent message overlay. It provides key calls-to-action to guide user interaction.

  • Background video

    • Plays automatically, loops continuously, and remains muted
    • Provides a modern, dynamic visual to capture attention
  • Fallback image

    • Displays when video is unsupported or loading
    • Ensures consistent visual presentation across all devices
  • Overlay content

    • Bold headline to introduce the brand message
    • Descriptive paragraph explaining the service value
    • Two prominent buttons:
      • “See solutions” directs users to the shipping services section
      • “Talk to us” connects users to the support section
  • Responsive layout

    • Content and visuals adjust gracefully across screen sizes
    • Ensures readability and interaction on both desktop and mobile devices

Responsiveness

The layout uses Bootstrap grid and utility classes to ensure mobile-friendliness:

  • Text and buttons are center-aligned
  • Video and image scale responsively to fit different screen sizes
  • Overlay adapts its size and alignment on smaller viewports

Additional Notes

The banner section is optimized for performance and accessibility.

  • Autoplay settings comply with modern browser policies.
  • Fallback visuals ensure compatibility.
  • The calls-to-action are placed to drive engagement and support navigation flow.

The carousel section features an auto-sliding image gallery with manual navigation controls for a dynamic user experience.

  • Automatic slide transition

    • Slides change every 3 seconds
    • Pauses on hover is disabled for continuous motion
  • Navigation buttons

    • Previous and Next buttons allow manual navigation
    • Icons for intuitive control
  • Indicators

    • Shows clickable buttons to jump to specific slides
    • Active slide is clearly highlighted
  • Responsive design

    • Ensures smooth experience across all devices
    • Images scale accordingly for different screen sizes

Country Dropdown

Dynamically Populate Countries

To dynamically populate the country dropdown, I used a country-fetching Javscript:

  • Fetch the list of countries using the Intl.DisplayNames API.
  • Sort them alphabetically.
  • Inject them into the <select id="country"> dropdown.

Initial script:

document.addEventListener("DOMContentLoaded", function () {
const countrySelect = document.getElementById("country");

const placeholderOption = countrySelect.options[0];
countrySelect.innerHTML = "";
countrySelect.appendChild(placeholderOption);

// Get country names using Intl API
const regionNames = new Intl.DisplayNames(['en'], { type: 'region' });

const countries = Array.from(
new Set(
Intl.supportedValuesOf('region')
.filter(code => /^[A-Z]{2}$/.test(code)) // Ensure ISO country codes
)
).map(code => regionNames.of(code));

// Sort alphabetically
countries.sort((a, b) => a.localeCompare(b));

// Append countries to the dropdown
countries.forEach(country => {
const option = document.createElement("option");
option.value = country;
option.textContent = country;
countrySelect.appendChild(option);
});
});

Then configured the section in HTML:

<body>
<!-- Other sections above -->

<div class="form-group">
<div class="custom-select
supp-sec-3-select-wrapper">
<select class="form-control"
id="country">
<option value="" disabled selected>Country/Region</option>
</select>
<img src="./icons/arrow-down.svg" class="dropdown-arrow" alt="arrow icon">
</div>
</div>

<!-- Other sections below -->

<script src="./src/js/fetch-countries.js"></script>

</body>

To use this approach, the browser must support the Intl.DisplayNames and Intl.supportedValuesOf APIs (available in Chrome 102+, Edge 102+, Firefox 103+, Safari 15.4+). I gave it a try, but it didn’t work, so I did some troubleshooting:

  • Opened the browser’s DevTools → Console
  • Noticed errors like Uncaught ReferenceError and 404s in the Network tab

To confirm support, run this in the console:

Intl.supportedValuesOf('region')

If your browser doesn’t support it, you’ll see an error like this:

Screenshot of error

Or from the script:

fetch-countries.js:14 Uncaught RangeError: Invalid key : region
at Intl.supportedValuesOf (<anonymous>)
at HTMLDocument.<anonymous> (fetch-countries.js:14:12)
(anonymous) @ fetch-countries.js:14

Error:

Uncaught RangeError: Invalid key : region

This confirms the API isn’t supported in the current browser.

Workaround: Use a fallback list of country codes instead.

Using a Static Country List

Instead of using an API, I opted for using a static list of country codes. The script below populates a select dropdown with all countries using ISO 3166-1 alpha-2 codes and their display names.

  • Generates country names from ISO codes
  • Clears default options except placeholder
  • Sorts country names alphabetically
  • Appends each country as an option in the dropdown

This provides accurate, localized country listing without external APIs.

document.addEventListener("DOMContentLoaded", function () {
const countrySelect = document.getElementById("country");

// Save and clear the placeholder
const placeholder = countrySelect.querySelector("option[disabled]");
countrySelect.innerHTML = "";
countrySelect.appendChild(placeholder);

const regionNames = new Intl.DisplayNames(['en'], { type: 'region' });

// Full ISO 3166-1 alpha-2 country code list
const countryCodes = [
"AF","AX","AL","DZ","AS","AD","AO","AI","AQ","AG","AR","AM","AW","AU","AT","AZ",
"BS","BH","BD","BB","BY","BE","BZ","BJ","BM","BT","BO","BQ","BA","BW","BV","BR",
"IO","BN","BG","BF","BI","KH","CM","CA","CV","KY","CF","TD","CL","CN","CX","CC",
"CO","KM","CG","CD","CK","CR","CI","HR","CU","CW","CY","CZ","DK","DJ","DM","DO",
"EC","EG","SV","GQ","ER","EE","SZ","ET","FK","FO","FJ","FI","FR","GF","PF","TF",
"GA","GM","GE","DE","GH","GI","GR","GL","GD","GP","GU","GT","GG","GN","GW","GY",
"HT","HM","VA","HN","HK","HU","IS","IN","ID","IR","IQ","IE","IM","IL","IT","JM",
"JP","JE","JO","KZ","KE","KI","KP","KR","KW","KG","LA","LV","LB","LS","LR","LY",
"LI","LT","LU","MO","MG","MW","MY","MV","ML","MT","MH","MQ","MR","MU","YT","MX",
"FM","MD","MC","MN","ME","MS","MA","MZ","MM","NA","NR","NP","NL","NC","NZ","NI",
"NE","NG","NU","NF","MK","MP","NO","OM","PK","PW","PS","PA","PG","PY","PE","PH",
"PN","PL","PT","PR","QA","RE","RO","RU","RW","BL","SH","KN","LC","MF","PM","VC",
"WS","SM","ST","SA","SN","RS","SC","SL","SG","SX","SK","SI","SB","SO","ZA","GS",
"SS","ES","LK","SD","SR","SJ","SE","CH","SY","TW","TJ","TZ","TH","TL","TG","TK",
"TO","TT","TN","TR","TM","TC","TV","UG","UA","AE","GB","US","UM","UY","UZ","VU",
"VE","VN","VG","VI","WF","EH","YE","ZM","ZW"
];

const countries = countryCodes.map(code => ({
code,
name: regionNames.of(code)
}));

countries.sort((a, b) => a.name.localeCompare(b.name));

countries.forEach(({ name }) => {
const option = document.createElement("option");
option.value = name;
option.textContent = name;
countrySelect.appendChild(option);
});
});

Switching from ID to Class

To improve reusability, I updated the script to use a class selector instead of a specific ID. This allows the same logic to apply to multiple dropdowns on the page.

  • Replaced getElementById with querySelectorAll to select all elements with a specific class
  • Used forEach to loop through and populate each matching dropdown

This version supports multiple dropdowns with the class country-select, which makes the script modular and reusable across different parts of the page.

document.addEventListener("DOMContentLoaded", function () {
const selects = document.querySelectorAll(".country-select");
const regionNames = new Intl.DisplayNames(['en'], { type: 'region' });
const countryCodes = [ /* ISO 3166-1 alpha-2 list here */ ];

const countries = countryCodes.map(code => ({
code,
name: regionNames.of(code)
})).sort((a, b) => a.name.localeCompare(b.name));

selects.forEach(countrySelect => {
const placeholder = countrySelect.querySelector("option[disabled]");
countrySelect.innerHTML = "";
if (placeholder) countrySelect.appendChild(placeholder);

countries.forEach(({ name }) => {
const option = document.createElement("option");
option.value = name;
option.textContent = name;
countrySelect.appendChild(option);
});
});
});

Responsive Web Design

Common Screen Widths

Device TypeScreen Width (CSS px)Example Devices
Small phones320pxiPhone SE (1st gen), older Androids
Compact phones360px – 375pxiPhone SE (2022), Pixel 4a, Galaxy A series
Average smartphones390px – 414pxiPhone 13/14, Galaxy S23, Pixel 7
Tablets / Large phones428px – 480pxiPhone 15 Pro Max, Galaxy Note series

Recommendations:

  • Use 320px as a baseline
  • 360px is the modern safe minimum
  • Test with breakpoints like max-width: 575.98px, 480px, and 360px

DevTools Emulation

Used these values to add custom devices in Chrome DevTools:

DeviceViewport (px)Device Pixel RatioResolution (px)
Z Flip (2020 & 5G)412 × 10042.6251080 × 2636
Z Flip 3 / 4 / 5412 × 9153.01080 × 2640
Samsung Galaxy S23+412 × 9153.01080 × 2340
Samsung Galaxy S24412 × 9153.01080 × 2340
iPhone 16 Pro Max430 × 9323.01290 × 2796 (est.)
iPhone 14390 × 8443.01170 × 2532

Adding a Custom Device

To simulate the device in Chrom DevTools:

  1. Open DevTools → Toggle device toolbar
  2. Click device dropdown → Edit → "Add custom device"
  3. Fill in name, width, height, and DPR as above

Feedback