Jamie Balfour

Welcome to my personal website.

Find out more about me, my personal projects, reviews, courses and much more here.

Jamie Balfour'sPersonal blog

Jamie Balfour'sPersonal blog

In the last few hours, I have been working on improving the information architecture and the URLs for my tutorials. For the last six or seven years the way the URLs to my tutorials have looked is like this:

/courses/web/css/1/2/

Now, with a bit of work, I've written a much more beautiful system, and that makes the URLs look like this:

/courses/web/css/using_css/

The new system uses URL rewrites alongside a clever automated (cron) generated JSON in which all of the titles are given friendly names:

JSON
[{
  "page_name": "What this tutorial is",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/1/1",
  "area": "1",
  "page": "1",
  "title": "what_this_tutorial_is",
  "friendly_path": "web/css/what_this_tutorial_is"
}, {
  "page_name": "What CSS is",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/2/1",
  "area": "2",
  "page": "1",
  "title": "what_css_is",
  "friendly_path": "web/css/what_css_is"
}, {
  "page_name": "Using CSS",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/2/2",
  "area": "2",
  "page": "2",
  "title": "using_css",
  "friendly_path": "web/css/using_css"
}, {
  "page_name": "Why CSS is used",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/2/3",
  "area": "2",
  "page": "3",
  "title": "why_css_is_used",
  "friendly_path": "web/css/why_css_is_used"
}, {
  "page_name": "Classes, IDs and tag selection",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/2/4",
  "area": "2",
  "page": "4",
  "title": "classes_ids_and_tag_selection",
  "friendly_path": "web/css/classes_ids_and_tag_selection"
}, {
  "page_name": "Display, positioning, floating and visibility",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/3/1",
  "area": "3",
  "page": "1",
  "title": "display_positioning_floating_and_visibility",
  "friendly_path": "web/css/display_positioning_floating_and_visibility"
}, {
  "page_name": "Width, height, padding, margins and overflow",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/3/2",
  "area": "3",
  "page": "2",
  "title": "width_height_padding_margins_and_overflow",
  "friendly_path": "web/css/width_height_padding_margins_and_overflow"
}, {
  "page_name": "Borders, box shadows, border radius and box sizing",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/3/3",
  "area": "3",
  "page": "3",
  "title": "borders_box_shadows_border_radius_and_box_sizing",
  "friendly_path": "web/css/borders_box_shadows_border_radius_and_box_sizing"
}, {
  "page_name": "Color, backgrounds, opacity and gradients",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/3/4",
  "area": "3",
  "page": "4",
  "title": "color_backgrounds_opacity_and_gradients",
  "friendly_path": "web/css/color_backgrounds_opacity_and_gradients"
}, {
  "page_name": "Font families, sizes, weights, variants, styles and alignment",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/3/5",
  "area": "3",
  "page": "5",
  "title": "font_families_sizes_weights_variants_styles_and_alignment",
  "friendly_path": "web/css/font_families_sizes_weights_variants_styles_and_alignment"
}, {
  "page_name": "Descendant selector",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/4/1",
  "area": "4",
  "page": "1",
  "title": "descendant_selector",
  "friendly_path": "web/css/descendant_selector"
}, {
  "page_name": "Child selector",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/4/2",
  "area": "4",
  "page": "2",
  "title": "child_selector",
  "friendly_path": "web/css/child_selector"
}, {
  "page_name": "Adjacenct sibling selector",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/4/3",
  "area": "4",
  "page": "3",
  "title": "adjacenct_sibling_selector",
  "friendly_path": "web/css/adjacenct_sibling_selector"
}, {
  "page_name": "General sibling selector",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/4/4",
  "area": "4",
  "page": "4",
  "title": "general_sibling_selector",
  "friendly_path": "web/css/general_sibling_selector"
}, {
  "page_name": "Universal selector",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/4/5",
  "area": "4",
  "page": "5",
  "title": "universal_selector",
  "friendly_path": "web/css/universal_selector"
}, {
  "page_name": "Attribute selector",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/4/6",
  "area": "4",
  "page": "6",
  "title": "attribute_selector",
  "friendly_path": "web/css/attribute_selector"
}, {
  "page_name": "An introduction to pseudo-selectors",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/5/1",
  "area": "5",
  "page": "1",
  "title": "an_introduction_to_pseudo-selectors",
  "friendly_path": "web/css/an_introduction_to_pseudo-selectors"
}, {
  "page_name": "Advanced CSS pseudo selectors",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/5/2",
  "area": "5",
  "page": "2",
  "title": "advanced_css_pseudo_selectors",
  "friendly_path": "web/css/advanced_css_pseudo_selectors"
}, {
  "page_name": "Shorthand CSS",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/6/1",
  "area": "6",
  "page": "1",
  "title": "shorthand_css",
  "friendly_path": "web/css/shorthand_css"
}, {
  "page_name": "Precedence and specificity",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/6/2",
  "area": "6",
  "page": "2",
  "title": "precedence_and_specificity",
  "friendly_path": "web/css/precedence_and_specificity"
}, {
  "page_name": "CSS rules",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/6/3",
  "area": "6",
  "page": "3",
  "title": "css_rules",
  "friendly_path": "web/css/css_rules"
}, {
  "page_name": "Optimising CSS delivery",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/6/4",
  "area": "6",
  "page": "4",
  "title": "optimising_css_delivery",
  "friendly_path": "web/css/optimising_css_delivery"
}, {
  "page_name": "Cross platform and browser compatibility",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/6/5",
  "area": "6",
  "page": "5",
  "title": "cross_platform_and_browser_compatibility",
  "friendly_path": "web/css/cross_platform_and_browser_compatibility"
}, {
  "page_name": "Server-side generated CSS",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/6/6",
  "area": "6",
  "page": "6",
  "title": "server-side_generated_css",
  "friendly_path": "web/css/server-side_generated_css"
}, {
  "page_name": "CSS and Sass",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/6/7",
  "area": "6",
  "page": "7",
  "title": "css_and_sass",
  "friendly_path": "web/css/css_and_sass"
}, {
  "page_name": "An introduction to responsive web design",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/7/1",
  "area": "7",
  "page": "1",
  "title": "an_introduction_to_responsive_web_design",
  "friendly_path": "web/css/an_introduction_to_responsive_web_design"
}, {
  "page_name": "Tablet and smartphone friendly CSS",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/7/2",
  "area": "7",
  "page": "2",
  "title": "tablet_and_smartphone_friendly_css",
  "friendly_path": "web/css/tablet_and_smartphone_friendly_css"
}, {
  "page_name": "CSS transform",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/8/1",
  "area": "8",
  "page": "1",
  "title": "css_transform",
  "friendly_path": "web/css/css_transform"
}, {
  "page_name": "CSS transitions",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/8/2",
  "area": "8",
  "page": "2",
  "title": "css_transitions",
  "friendly_path": "web/css/css_transitions"
}, {
  "page_name": "CSS counters",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/8/3",
  "area": "8",
  "page": "3",
  "title": "css_counters",
  "friendly_path": "web/css/css_counters"
}]

On the front end, the page is found via its friendly_path value. To keep the order correct, the pages are not given a key. Although it would make it slightly faster to give them this key, it would mean that the order isn't correct any longer.

Changes to title names will actually break the system and any changes made will need to be followed by a running of the automated script (which happens every evening anyway).

I'm still testing this feature out, so if you find any bugs, let me know. 

Powered by DASH 2.0