[{"data":1,"prerenderedAt":2334},["ShallowReactive",2],{"gate-notes":3,"layout-site":232,"layout-footer":253,"gate-uses":261,"gate-gallery":421,"project-\u002Fprojects\u002Ffurni":436,"projects-siblings":527},[4,113],{"id":5,"title":6,"body":7,"cover":94,"date":99,"description":13,"draft":100,"extension":101,"meta":102,"navigation":103,"path":104,"placeholder":100,"readingMinutes":92,"seo":105,"stem":106,"summary":107,"tags":108,"__hash__":112},"blog\u002Fblog\u002Fthe-future-of-programming-learn-to-think-not-just-code.md","The Future of Programming: Learn to Think, Not Just Code",{"type":8,"value":9,"toc":90},"minimark",[10,14,17,20,23,26,29,32,35,38,41,44,69,72,75,78,81,84,87],[11,12,13],"p",{},"The era of writing every line of code by hand is slowly coming to an end.",[11,15,16],{},"AI can already generate code faster than most developers, and it is becoming\nsurprisingly capable at finding bugs, reviewing implementations, and suggesting\nimprovements. As these systems continue to evolve, even traditional code review\nwill require less human involvement.",[11,18,19],{},"So, does that mean we no longer need to learn programming?",[11,21,22],{},"Quite the opposite.",[11,24,25],{},"We still need to understand programming—but the way we learn it has to change.",[11,27,28],{},"For years, learning programming meant spending countless hours writing syntax,\nmemorizing APIs, debugging errors, and building things line by line. That\napproach made sense when humans were responsible for producing the code.",[11,30,31],{},"But if AI is going to handle much of the implementation, our focus needs to move\nfrom writing code to understanding systems.",[11,33,34],{},"Think about an architect. An architect doesn't need to personally lay every\nbrick, install every pipe, or wire every electrical connection. Their job is to\nunderstand the entire structure—how different parts fit together, where problems\nmight occur, what materials should be used, and how everything should work\ntogether.",[11,36,37],{},"They understand the system so they can make the right decisions.",[11,39,40],{},"The same shift is happening in software development.",[11,42,43],{},"AI may write the implementation, but someone still needs to decide:",[45,46,47,51,54,57,60,63,66],"ul",{},[48,49,50],"li",{},"What should be built?",[48,52,53],{},"Why should it be built this way?",[48,55,56],{},"How should the system behave?",[48,58,59],{},"Which architecture makes sense?",[48,61,62],{},"What trade-offs are acceptable?",[48,64,65],{},"How should different components interact?",[48,67,68],{},"What could go wrong?",[11,70,71],{},"That person needs to understand programming deeply enough to make those\ndecisions—even if they aren't personally typing every line.",[11,73,74],{},"In other words, the developer of the future may look less like a traditional\ncoder and more like a technical strategist or team lead.",[11,76,77],{},"So don't stop learning programming.",[11,79,80],{},"Instead, change what you are learning programming for.",[11,82,83],{},"Learn the logic, architecture, systems, patterns, trade-offs, and problem-solving\nbehind the code.",[11,85,86],{},"Because the future may not belong to the people who can write the most code.",[11,88,89],{},"It may belong to the people who understand what code needs to exist—and why.",{"title":91,"searchDepth":92,"depth":92,"links":93},"",3,[],{"src":95,"alt":96,"width":97,"height":98,"caption":91},"\u002Fimages\u002Fblog\u002Fthe-future-of-programming-learn-to-think-not-just-code.webp","A developer at a whiteboard of system decisions beside a robot writing code on a laptop, captioned \"not just a coder, a technical strategist\"",1600,900,"2026-09-05",false,"md",{},true,"\u002Fblog\u002Fthe-future-of-programming-learn-to-think-not-just-code",{"title":6,"description":13},"blog\u002Fthe-future-of-programming-learn-to-think-not-just-code","AI is getting good enough at implementation that writing every line by hand is ending. That is not a reason to stop learning programming — it is a reason to change what you are learning it for.",[109,110,111],"ai","career","engineering","Id7FK6pS1qhGW_lDGNt5ThD6jL1sjywZu573G5SV908",{"id":114,"title":115,"body":116,"cover":217,"date":220,"description":91,"draft":100,"extension":101,"meta":221,"navigation":103,"path":222,"placeholder":100,"readingMinutes":223,"seo":224,"stem":225,"summary":226,"tags":227,"__hash__":231},"blog\u002Fblog\u002Fwhat-renaming-a-plugin-actually-touches.md","What renaming a plugin actually touches",{"type":8,"value":117,"toc":209},[118,123,126,129,132,136,144,176,179,183,186,189,192,196,199,202,206],[119,120,122],"h2",{"id":121},"the-second-rename-was-not-my-idea","The second rename was not my idea",[11,124,125],{},"Defer Forms for Contact Form 7 has had two names before this one. The first rename\nwas mine — the old name said nothing about what the plugin did, and its folder name\nbroke a wordpress.org naming rule I'd rather have caught earlier. That one I chose,\non my own schedule.",[11,127,128],{},"The second was not a choice. It went into wordpress.org review under its new name\nand came back rejected: the name led with a generic word and sat too close to an\nexisting plugin's. Review doesn't negotiate. You get a reason and a rename.",[11,130,131],{},"The part worth writing down isn't the naming — plenty has been written about naming\nthings. It's that a rename looks like a one-line change and isn't one, the moment\nthe plugin has been installed anywhere.",[119,133,135],{"id":134},"a-name-is-not-just-a-label","A name is not just a label",[11,137,138,139,143],{},"The visible rename — the string in the plugin header, the text in the admin menu —\ntook an afternoon. Everywhere else the old name was actually ",[140,141,142],"em",{},"encoded"," took four\nmore releases to find and fix, one at a time:",[45,145,146,153,164,170],{},[48,147,148,152],{},[149,150,151],"strong",{},"The database table."," Submissions, roles, everything the plugin stores lived\nunder a table prefixed with the old internal short name.",[48,154,155,158,159,163],{},[149,156,157],{},"The developer hooks."," Seven of them, documented, with real signatures — anyone\nwho had already filtered on ",[160,161,162],"code",{},"old_prefix_submission_created"," needed to know it was\nnow something else, not silently stop firing.",[48,165,166,169],{},[149,167,168],{},"The REST namespace."," Every request the builder makes goes through it. Miss one\nreference and the admin looks fine until you open the one screen that calls it.",[48,171,172,175],{},[149,173,174],{},"Post meta keys, script and style handles, admin page URLs."," The last one meant\ntelling people to update their own bookmarks — a genuinely small cost, and still\na cost, and still something I had to remember to say.",[11,177,178],{},"None of this shows up in a diff of the plugin's header comment. All of it is the\nactual rename.",[119,180,182],{"id":181},"the-migration-has-to-run-once-and-only-once","The migration has to run once, and only once",[11,184,185],{},"The database table was the one that mattered most, because it's the one with a\nvisitor's data in it. Renaming a table isn't a decision you make in code and walk\naway from — every install that already has the old table needs something to run,\nonce, that moves its data to the new one before anything tries to read from the new\nname and finds nothing there.",[11,187,188],{},"That migration shipped in the same release as the prefix change. It checked for the\nold table, moved what it found, and got out of the way — the kind of code that is\neasy to write defensively and easy to forget about afterward, because once it's\nshipped it mostly just sits there working correctly for installs that need it and\ndoing nothing for installs that don't.",[11,190,191],{},"Sitting there is not free, though. Every release after that carried one more piece\nof logic whose entire job was reasoning about a table name nothing was going to use\ngoing forward. That's a real, if small, tax — one more thing to read when you're\ntrying to understand what a fresh install actually does, one more thing that could\nin principle misfire against data it was never meant to touch.",[119,193,195],{"id":194},"knowing-when-its-safe-to-delete","Knowing when it's safe to delete",[11,197,198],{},"The migration came out four releases later, and the reason it was safe to remove is\nthe part I'd actually recommend paying attention to: not \"it's probably fine by\nnow,\" but a specific, checkable claim — this plugin has never been distributed\nunder its old internal prefix. Every install anyone could download from that point\non was already past the rename. There was no table for the migration to ever find,\non any install that didn't already exist before the code was written, and there\nnever would be one again.",[11,200,201],{},"That's a different thing from \"nobody's complained.\" It's a claim about the\ndistribution history, and it's the kind of claim you can actually check rather than\nassume. Dead code that runs a permission check first and does nothing is safe. Dead\ncode you're only fairly sure is dead is a liability wearing the same clothes.",[119,203,205],{"id":204},"what-id-do-differently","What I'd do differently",[11,207,208],{},"Rename the internal prefix in the same release as the visible name, not four\nreleases later. Splitting them meant the plugin spent a stretch of its life with a\npublic name that didn't match anything in its own codebase — which is confusing to\nread, and is exactly the kind of gap a second, unplanned rename can catch you\nin the middle of. The visible name is the one wordpress.org reviews. The internal\none is the one that actually has to be right, and it deserves to move on the same\nday.",{"title":91,"searchDepth":92,"depth":92,"links":210},[211,213,214,215,216],{"id":121,"depth":212,"text":122},2,{"id":134,"depth":212,"text":135},{"id":181,"depth":212,"text":182},{"id":194,"depth":212,"text":195},{"id":204,"depth":212,"text":205},{"src":218,"alt":219,"width":97,"height":98},"\u002Fimages\u002Fblog\u002Fwhat-renaming-a-plugin-actually-touches.webp","Abstract illustration for an article about renaming a WordPress plugin and migrating its internal identity","2026-09-03",{},"\u002Fblog\u002Fwhat-renaming-a-plugin-actually-touches",6,{"title":115,"description":91},"blog\u002Fwhat-renaming-a-plugin-actually-touches","A wordpress.org review rejected the name once. The rename itself was the easy part — the database table, the hooks API, and the REST namespace all had to move too, for a plugin that already had real installs.",[228,229,230],"wordpress","php","products","yyGeKgB7V-MTe1mpjFtKFNbd162HcN-P5NvkBD-dNpA",{"id":233,"email":234,"extension":235,"meta":236,"name":237,"role":238,"socials":239,"stem":249,"summary":250,"whatsapp":251,"__hash__":252},"site\u002Fglobal\u002Fsite.json","rupash.das.40@gmail.com","json",{},"Rupash Das","WordPress developer",[240,243,246],{"label":241,"url":242},"GitHub","https:\u002F\u002Fgithub.com\u002FRupashdas",{"label":244,"url":245},"WordPress.org","https:\u002F\u002Fprofiles.wordpress.org\u002Frupash\u002F",{"label":247,"url":248},"Fiverr","https:\u002F\u002Fwww.fiverr.com\u002Frupash_das","global\u002Fsite","Around 45 production WordPress sites shipped as a US agency's development partner, two plugins of my own on WordPress.org, and eight years of client work underneath — with Laravel and Vue for the projects that outgrow a CMS.","https:\u002F\u002Fwa.me\u002F8801873139202","cwtV8IanoYf5Y-mEYGx1kshNo8fuI44ktzHQ-exTG3Q",{"id":254,"colophon":255,"ctaLabel":256,"extension":235,"meta":257,"stem":258,"tagline":259,"__hash__":260},"footer\u002Fglobal\u002Ffooter.json","Rupash Das · Built with Nuxt","Start a project",{},"global\u002Ffooter","WordPress plugins, themes and sites. Built in Bangladesh, shipped worldwide.","x4IfCEQxZIviX8mt3G3dP5qBj4pD3SvSETmd231CzCY",[262,291,316,338,370,395],{"id":263,"category":264,"extension":235,"items":265,"meta":286,"order":287,"stem":288,"summary":289,"__hash__":290},"uses\u002Fpages\u002Fuses\u002F01-applications.json","Beyond WordPress",[266,270,274,278,282],{"name":267,"url":268,"note":269,"key":103},"Laravel","https:\u002F\u002Flaravel.com","The framework for anything that has outgrown a CMS. Prices and stock decided on the server, and feature tests that run against the real database rather than a mock of it.",{"name":271,"url":272,"note":273,"key":103},"Vue 3, Composition API","https:\u002F\u002Fvuejs.org","Composition over Options for anything non-trivial. Logic that belongs together lives together, and it can be pulled out into a composable the day a second screen needs it.",{"name":275,"url":276,"note":277},"Vite","https:\u002F\u002Fvite.dev","The build for everything — applications and plugin interfaces both. Fast enough that a React panel inside wp-admin stops feeling like a compromise.",{"name":279,"url":280,"note":281},"Tailwind","https:\u002F\u002Ftailwindcss.com","Tokens in one file, utilities everywhere else. The token layer is the part that matters; the utilities are only how it gets applied.",{"name":283,"url":284,"note":285},"Nuxt","https:\u002F\u002Fnuxt.com","This site. Prerendered, with a bundle budget that fails the build rather than being admired in a readme.",{},4,"pages\u002Fuses\u002F01-applications","What I reach for when a project outgrows a CMS and needs to be an application of its own.","AYQQ46_MRZtaMpLllg18x-vIKFwTOCQVKFrDv3xY7F4",{"id":292,"category":293,"extension":235,"items":294,"meta":312,"order":212,"stem":313,"summary":314,"__hash__":315},"uses\u002Fpages\u002Fuses\u002F02-local.json","Local development",[295,299,303,306,309],{"name":296,"url":297,"note":298,"key":103},"LocalWP","https:\u002F\u002Flocalwp.com","One throwaway site per plugin. A plugin has to be installed on a clean WordPress before I believe it works.",{"name":300,"url":301,"note":302,"key":103},"Laragon","https:\u002F\u002Flaragon.org","Where the Laravel side lives. Apache, PHP and MySQL without an afternoon of configuration.",{"name":304,"note":305},"MySQL","The database under both stacks. Enough of it that a data migration can be a query rather than a plugin.",{"name":307,"note":308,"key":103},"A test suite that runs locally","Just over a hundred suites on the form plugin alone. They exist because it ships to sites I will never see, and no amount of clicking replaces that.",{"name":310,"note":311},"A guarded release script","Building a release by hand is how a stray development file ends up in a zip. The script refuses rather than reminds me.",{},"pages\u002Fuses\u002F02-local","Two stacks, kept apart. WordPress work and application work do not share an environment.","jjnVioJgu2KQ94wsXA4f4vcf7TDjMf1p0nKZskI2C6k",{"id":317,"category":318,"extension":235,"items":319,"meta":334,"order":92,"stem":335,"summary":336,"__hash__":337},"uses\u002Fpages\u002Fuses\u002F03-terminal.json","Version control and terminal",[320,323,326,328,331],{"name":321,"note":322,"key":103},"Git","Every project is a repository, including the client sites that were never supposed to have one. It is also the reason I can work on a live site and still have a way back.",{"name":324,"note":325,"key":103},"Git Bash on Windows Terminal","POSIX habits on a Windows machine. Most tooling assumes a Unix shell, and it is less work to bring one than to translate every command.",{"name":241,"url":242,"note":327},"Public for the plugins, private for client work. Actions for anything that has to happen on a schedule.",{"name":329,"note":330},"Composer","PHP dependencies, and increasingly the autoloader inside plugins that used to get by without one.",{"name":332,"note":333},"npm","The other half of every build. Kept boring on purpose — a dependency in a plugin is a dependency in somebody's site.",{},"pages\u002Fuses\u002F03-terminal","The layer underneath everything else. Unglamorous, and the part I would miss first.","T-s-xcuuGET_9dm2M0uVTuhlZPPSeCAvRAiH65e1P4w",{"id":339,"category":340,"extension":235,"items":341,"meta":365,"order":366,"stem":367,"summary":368,"__hash__":369},"uses\u002Fpages\u002Fuses\u002F04-wordpress.json","The WordPress side",[342,346,350,353,357,361],{"name":343,"url":344,"note":345,"key":103},"React with @wordpress\u002Fcomponents","https:\u002F\u002Fdeveloper.wordpress.org\u002Fblock-editor\u002Freference-guides\u002Fcomponents\u002F","Plugin interfaces are applications mounted inside wp-admin. Using the WordPress components is why they look like they belong there rather than like something bolted on.",{"name":347,"url":348,"note":349,"key":103},"dnd-kit","https:\u002F\u002Fdndkit.com","Dragging that works from a keyboard and announces itself to a screen reader. Retrofitting that is far more expensive than choosing it.",{"name":351,"note":352,"key":103},"The WordPress REST API","The only seam between a plugin's PHP and its interface. It is what makes the PHP testable without a browser anywhere near it.",{"name":354,"url":355,"note":356},"Advanced Custom Fields Pro","https:\u002F\u002Fwww.advancedcustomfields.com","For client sites, where the content model lives. Fields named in the client's vocabulary, not the database's.",{"name":358,"url":359,"note":360},"Elementor","https:\u002F\u002Felementor.com","Not a compromise — the right answer when an in-house team edits weekly. Locked templates, a small palette, global styles set once.",{"name":362,"url":363,"note":364},"WooCommerce","https:\u002F\u002Fwoocommerce.com","With as few extension plugins as the requirement genuinely needs. Every add-on is a licence somebody has to keep paying.",{},1,"pages\u002Fuses\u002F04-wordpress","What goes into a plugin, and what goes onto a client site. Two different lists with two different standards.","j-NSUhkpvsb9v4-rYLa2FhTlS18t8gWF99Jgv4J-yDg",{"id":371,"category":372,"extension":235,"items":373,"meta":390,"order":391,"stem":392,"summary":393,"__hash__":394},"uses\u002Fpages\u002Fuses\u002F05-services.json","Hosting and delivery",[374,377,380,384,387],{"name":375,"note":376,"key":103},"The client's own hosting","Shared hosting, a control panel, no staging environment and no deploy pipeline. That is the honest description of most WordPress work, and pretending otherwise is how a rebuild gets quoted when a fix was needed.",{"name":378,"note":379,"key":103},"FTP against production","Not a preference, a constraint of agency work. It teaches one discipline above all: change one thing, verify it, keep a way back.",{"name":381,"url":382,"note":383,"key":103},"Cloudflare","https:\u002F\u002Fcloudflare.com","DNS, CDN and Pages. This site is prerendered and served from it.",{"name":385,"note":386},"A transactional email provider","Site mail leaves through a real provider, never the web host's PHP mail. This is the single most common reason a contact form fails silently.",{"name":388,"note":389},"Backups the client owns","To storage in their account, not mine. Leaving me should never mean leaving their data.",{},5,"pages\u002Fuses\u002F05-services","Mostly other people's infrastructure. The skill is working inside constraints I did not choose.","FbmQ8ReFvSj2JxfK-J7EKTve-3XXK3FlX2rLGYsSX4s",{"id":396,"category":397,"extension":235,"items":398,"meta":417,"order":223,"stem":418,"summary":419,"__hash__":420},"uses\u002Fpages\u002Fuses\u002F06-working.json","How I work",[399,402,405,408,411,414],{"name":400,"note":401,"key":103},"Model the data before the interface","The fastest way to make something expensive to own is to build the screens first and discover the shape of the data afterwards. True of a Laravel schema and of an ACF field group alike.",{"name":403,"note":404,"key":103},"Read the source before the documentation","Learned in two years of having nobody to ask. It is still the fastest route into an unfamiliar framework, and it is why picking up a new one is a week rather than a season.",{"name":406,"note":407,"key":103},"Ask who maintains it, before choosing how to build it","The same question opens every project. It decides page builder or custom theme, and it decides how much of an application belongs in its API rather than its front end.",{"name":409,"note":410},"Deep work in the morning, calls at night","US clients, Bangladesh time zone. The overlap is my evening, which leaves the mornings uninterrupted.",{"name":412,"note":413},"A repeated complaint is a specification","The same request from four unrelated clients is not an inconvenience. It is the only market research a one-person shop gets for free, and every plugin I have finished started there.",{"name":415,"note":416},"Write the handover in the maintainer's language","One of my plugins ships its maintainer notes in Bengali. Documentation somebody has to translate before using is documentation that will not be used.",{},"pages\u002Fuses\u002F06-working","Process rather than software. Most of it was learned the expensive way.","iaojSZmCa_ecT-IzSH3TzkN6K5Heshb2TELxA0DygII",[422],{"id":423,"title":424,"category":425,"extension":235,"image":426,"meta":432,"order":433,"stem":434,"__hash__":435},"gallery\u002Fpages\u002Fgallery\u002F06-plugin-settings.json","Settings, after the cuts","Plugins",{"src":427,"alt":428,"width":429,"height":430,"caption":431},"\u002Fimages\u002Fgallery\u002Fplugin-settings.webp","The spam settings screen in Defer Forms, with toggles for honeypot protection, a time-trap and duplicate-submission blocking",1400,1050,"Most of what a settings page wants to be belongs in the hooks API instead.",{},0,"pages\u002Fgallery\u002F06-plugin-settings","FnORlGVc8CiwZiDTKn47gc0jX559WJXPefZDoT_dpyA",{"id":437,"title":438,"body":439,"cover":467,"description":91,"eta":472,"extension":101,"facts":473,"featured":100,"featuredOrder":472,"kicker":486,"liveUrl":472,"meta":487,"metrics":488,"navigation":103,"order":489,"path":490,"placeholder":100,"repoUrl":472,"role":491,"sector":492,"seo":493,"shots":494,"stack":516,"status":521,"stem":522,"summary":523,"type":524,"year":525,"__hash__":526},"projects\u002Fprojects\u002Ffurni.md","FURNI",{"type":8,"value":440,"toc":462},[441,445,448,452,455,459],[119,442,444],{"id":443},"context","Context",[11,446,447],{},"FURNI is a furniture and home-decor eCommerce concept. The brief was a\n17-page storefront frontend: category browsing, search, product listings and\ndetail pages, a multi-step checkout, and full account management, all pixel\nmatched to a supplied design.",[119,449,451],{"id":450},"what-i-built","What I built",[11,453,454],{},"A responsive front end in HTML, CSS and JavaScript with Bootstrap: category\nand search pages, product listing and detail pages with a cart and wishlist,\na multi-step checkout (address, payment), and account pages, all matched to\na supplied design across every page.",[119,456,458],{"id":457},"why-its-here","Why it's here",[11,460,461],{},"Real, delivered client work: a large eCommerce frontend built to a supplied\ndesign, handed off rather than deployed by me — which is why it carries no\nlive link.",{"title":91,"searchDepth":92,"depth":92,"links":463},[464,465,466],{"id":443,"depth":212,"text":444},{"id":450,"depth":212,"text":451},{"id":457,"depth":212,"text":458},{"src":468,"alt":469,"width":470,"height":471},"\u002Fimages\u002Fprojects\u002Ffurni-cover.webp","The FURNI homepage hero, \"Modern Furniture for a Better Home\", above a popular categories grid and featured products row",1448,1086,null,[474,477,480,483],{"label":475,"value":476},"Role","Frontend developer",{"label":478,"value":479},"Client","FURNI Home & Living",{"label":481,"value":482},"Scope","17-page responsive site, frontend only",{"label":484,"value":485},"Stack","HTML5, CSS3, JS, Bootstrap","Client project · Frontend build",{},[],15,"\u002Fprojects\u002Ffurni","Frontend developer — build from a supplied design","Furniture & home decor eCommerce",{"title":438,"description":91},[495,501,506,511],{"src":496,"alt":497,"width":498,"height":499,"caption":500},"\u002Fimages\u002Fprojects\u002Ffurni-home.webp","The FURNI homepage with category tiles and featured products",1200,2476,"Home",{"src":502,"alt":503,"width":498,"height":504,"caption":505},"\u002Fimages\u002Fprojects\u002Ffurni-product-list.webp","The FURNI product listing page with filters",1778,"Product listing",{"src":507,"alt":508,"width":498,"height":509,"caption":510},"\u002Fimages\u002Fprojects\u002Ffurni-product-sheet.webp","A single product detail page on FURNI",1731,"Product detail",{"src":512,"alt":513,"width":498,"height":514,"caption":515},"\u002Fimages\u002Fprojects\u002Ffurni-cart.webp","The FURNI shopping cart page",999,"Cart",[517,518,519,520],"HTML5","CSS3","JavaScript","Bootstrap","complete","projects\u002Ffurni","A 17-page furniture and home-decor eCommerce frontend — category browsing, product listings, multi-step checkout and account management — built from a supplied design.","case-study","Delivered","VK53OMO_te-yjThK7Y3cTDmpvp06F0LTSZqpkY4LiVw",[528,648,698,751,1018,1204,1307,1613,1659,1734,1795,1859,1924,1988,2051,2086,2147,2206,2270],{"id":529,"title":530,"body":531,"cover":565,"description":91,"eta":472,"extension":101,"facts":570,"featured":103,"featuredOrder":472,"kicker":578,"liveUrl":579,"meta":580,"metrics":581,"navigation":103,"order":433,"path":582,"placeholder":100,"repoUrl":472,"role":583,"sector":584,"seo":585,"shots":586,"stack":640,"status":643,"stem":644,"summary":645,"type":524,"year":646,"__hash__":647},"projects\u002Fprojects\u002Fsorbet-magazine.md","Sorbet Magazine",{"type":8,"value":532,"toc":560},[533,535,538,541,543,549,555,557],[119,534,444],{"id":443},[11,536,537],{},"Sorbet is an independent quarterly fashion, art and culture magazine,\npublished in print and online. The site has to do two jobs. It is the\nmagazine online: daily articles, long-form interviews, photoshoots and a\nlarge video library. It is also the shop: annual subscriptions, the current\nissues in print and digital, and a catalogue of print back issues going back\nto 2013.",[11,539,540],{},"Both halves are run by an editorial team, not by developers. Editors publish\nseveral stories a week, and whatever they add has to look like the magazine\nwithout anyone laying out a page by hand.",[119,542,451],{"id":450},[11,544,545,548],{},[149,546,547],{},"The editorial site, in Elementor."," A homepage built from sections the\nteam keeps up to date: a cover-story hero slider, a Latest carousel, a Play\nvideo feature with circular video links, a Featured grid, a Photoshoot block\nand a strip of past covers leading into a subscription call to action. Behind\nit, one template for each kind of page rather than one-off layouts: a\ncategory archive (Fashion, Beauty, Lifestyle, Culture) with read-time and date\non every card, a paginated latest-posts archive, a single-article template\nthat handles both news posts and long interviews with inline captioned\nphotos, a video archive that loads more as you scroll, and a dark single-video\ntemplate with related videos below the player. A new post, category or video\npicks up the right layout the moment it is published.",[11,550,551,554],{},[149,552,553],{},"The store, in WooCommerce."," An issue shop with three kinds of product: an\nannual four-issue subscription, current issues sold as variable products\n(Digital, Print, or Digital plus Print, each with its own price), and print-only\nback issues in their own archive. It covers the whole purchase path, from\ncatalogue to product page to cart with coupons to checkout, plus a My Account\ntab that stays on screen across the store, all styled to match the magazine\nso buying an issue doesn't feel like leaving it.",[119,556,458],{"id":457},[11,558,559],{},"It is real, paid client work, live and published daily. It shows Elementor\nused as a system of reusable templates, not page by page, alongside a\nWooCommerce store that sells more than one simple kind of product. Most of\nthe Elementor and WooCommerce work I do is like this: built so the client's\nown team can keep running it.",{"title":91,"searchDepth":92,"depth":92,"links":561},[562,563,564],{"id":443,"depth":212,"text":444},{"id":450,"depth":212,"text":451},{"id":457,"depth":212,"text":458},{"src":566,"alt":567,"width":568,"height":569},"\u002Fimages\u002Fprojects\u002Fsorbet-cover.webp","A laptop and phone showing the Sorbet Magazine homepage, a \"The Fashion Issue\" hero above a row of latest articles, beside the Sorbet logo and the note \"built with Elementor + WooCommerce\"",1536,1024,[571,573,574,576],{"label":475,"value":572},"WordPress, Elementor & WooCommerce developer",{"label":478,"value":530},{"label":481,"value":575},"Magazine site and issue store",{"label":484,"value":577},"WordPress, Elementor Pro, WooCommerce","Client project · Elementor & WooCommerce build","https:\u002F\u002Fsorbetmagazine.com\u002F",{},[],"\u002Fprojects\u002Fsorbet-magazine","WordPress developer — Elementor templates, WooCommerce store, handoff","Fashion & lifestyle publishing",{"title":530,"description":91},[587,591,596,601,605,610,615,620,625,630,635],{"src":588,"alt":589,"width":498,"height":590,"caption":500},"\u002Fimages\u002Fprojects\u002Fsorbet-home.webp","The Sorbet Magazine homepage, with a full-width cover story hero, a Latest articles carousel, a Play video feature with a row of circular video links, a Featured grid, a Photoshoot section and a strip of past covers above an Order Now button",4659,{"src":592,"alt":593,"width":498,"height":594,"caption":595},"\u002Fimages\u002Fprojects\u002Fsorbet-store.webp","The Sorbet store page, with an annual subscription banner above a grid of print and digital issues and a second grid of print-only back issues, each cover showing its price",2071,"Store — subscriptions and issues",{"src":597,"alt":598,"width":498,"height":599,"caption":600},"\u002Fimages\u002Fprojects\u002Fsorbet-product.webp","The Fashion Issue product page, with the cover beside the issue description and a choice between Digital, Print, and Digital plus Print editions at different prices",803,"Issue — print or digital variants",{"src":602,"alt":603,"width":498,"height":604,"caption":515},"\u002Fimages\u002Fprojects\u002Fsorbet-cart.webp","The Sorbet cart with one digital issue, a coupon field and the cart total above a Proceed to checkout button",597,{"src":606,"alt":607,"width":498,"height":608,"caption":609},"\u002Fimages\u002Fprojects\u002Fsorbet-print-issues.webp","The print back-issue archive, a three-column grid of Sorbet covers from 2013 to 2015, each titled and priced",1379,"Back-issue archive",{"src":611,"alt":612,"width":498,"height":613,"caption":614},"\u002Fimages\u002Fprojects\u002Fsorbet-category.webp","The Fashion category page, a four-column grid of article cards, each with an image, category, read time, headline and date",2855,"Category archive",{"src":616,"alt":617,"width":498,"height":618,"caption":619},"\u002Fimages\u002Fprojects\u002Fsorbet-articles.webp","The Latest Post archive, a four-column grid of articles across fashion, beauty, culture and lifestyle, with numbered pagination",1819,"Latest posts",{"src":621,"alt":622,"width":498,"height":623,"caption":624},"\u002Fimages\u002Fprojects\u002Fsorbet-article.webp","A long-form interview article with a full-width portrait hero and headline, a byline and standfirst, then questions and answers set beside captioned photographs",3753,"Article",{"src":626,"alt":627,"width":498,"height":628,"caption":629},"\u002Fimages\u002Fprojects\u002Fsorbet-videos.webp","The Play video archive, a four-column grid of video thumbnails with titles set over each image",1406,"Video archive",{"src":631,"alt":632,"width":498,"height":633,"caption":634},"\u002Fimages\u002Fprojects\u002Fsorbet-video.webp","A single video page on a dark background, with an embedded YouTube player, the video title, and a row of related videos below",1286,"Video",{"src":636,"alt":637,"width":498,"height":638,"caption":639},"\u002Fimages\u002Fprojects\u002Fsorbet-about.webp","The Sorbet about page, a statement of what the magazine is beside the cover of The Performance Issue",814,"About",[641,642,362],"WordPress","Elementor Pro","live","projects\u002Fsorbet-magazine","An international fashion and culture magazine, built in WordPress with Elementor and WooCommerce — an editorial site with articles, photoshoots and video, plus a store that sells subscriptions, print and digital issues, and back issues.","2021","tcXEr78_FWdvANXJ23D5XAXKPbCpBljMPLwJGi80MMo",{"id":649,"title":650,"body":651,"cover":673,"description":91,"eta":472,"extension":101,"facts":677,"featured":103,"featuredOrder":472,"kicker":685,"liveUrl":686,"meta":687,"metrics":688,"navigation":103,"order":366,"path":689,"placeholder":100,"repoUrl":472,"role":690,"sector":691,"seo":692,"shots":693,"stack":694,"status":643,"stem":695,"summary":696,"type":524,"year":525,"__hash__":697},"projects\u002Fprojects\u002Fkestopelti.md","Kestopelti",{"type":8,"value":652,"toc":668},[653,655,658,660,663,665],[119,654,444],{"id":443},[11,656,657],{},"Kestopelti is a Finnish family-run manufacturer of steel roofing sheets, roof\naccessories and rainwater systems, shipping from factories in Joensuu and\nKuopio. This came in as a Fiverr order: a design already existed, and the job\nwas to turn it into a working front end.",[119,659,451],{"id":450},[11,661,662],{},"The design arrived as a finished file, not a brief — I wasn't the designer.\nMy part was the HTML, CSS and JavaScript: turning that design into a\nresponsive layout with Bootstrap, matched to the source file across\nbreakpoints, and handed off ready to be wired into whatever the client's team\nran on the backend. What that backend became, and who has touched the site\nsince, isn't something I can speak to — my scope stopped at the front end I\ndelivered.",[119,664,458],{"id":457},[11,666,667],{},"Most of what's on this page is bigger — an application, a plugin, years with\none agency. This is a single, smaller Fiverr order, and it earns its place\nfor a plainer reason: it's real, paid client work, delivered to spec, and the\nsite it became is still live today.",{"title":91,"searchDepth":92,"depth":92,"links":669},[670,671,672],{"id":443,"depth":212,"text":444},{"id":450,"depth":212,"text":451},{"id":457,"depth":212,"text":458},{"src":674,"alt":675,"width":97,"height":676},"\u002Fimages\u002Fprojects\u002Fkestopelti-cover.webp","The Kestopelti homepage, showing a full-bleed roofing photograph behind the headline \"Peltikatteet ja kattovarusteet\" and the site's top navigation",1000,[678,679,681,683],{"label":475,"value":476},{"label":478,"value":680},"Finnish roofing manufacturer",{"label":682,"value":247},"Delivered via",{"label":481,"value":684},"Frontend only","Fiverr client · Frontend build","https:\u002F\u002Fkestopelti.fi\u002F",{},[],"\u002Fprojects\u002Fkestopelti","Frontend developer — HTML\u002FCSS\u002FJS build from a supplied design","Roofing & building materials",{"title":650,"description":91},[],[517,518,519,520],"projects\u002Fkestopelti","The responsive front end for a Finnish steel-roofing manufacturer's site — coded from a supplied design and still live today.","MA6YKD-6qZIVPEVK5flDGlPDBbwf66_uAg2X5LiSp28",{"id":699,"title":700,"body":701,"cover":723,"description":91,"eta":472,"extension":101,"facts":728,"featured":103,"featuredOrder":472,"kicker":737,"liveUrl":738,"meta":739,"metrics":740,"navigation":103,"order":212,"path":741,"placeholder":100,"repoUrl":472,"role":742,"sector":743,"seo":744,"shots":745,"stack":746,"status":643,"stem":747,"summary":748,"type":524,"year":749,"__hash__":750},"projects\u002Fprojects\u002Fmiss-suomi.md","Miss Suomi Beauty Academy",{"type":8,"value":702,"toc":718},[703,705,708,710,713,715],[119,704,444],{"id":443},[11,706,707],{},"Miss Suomi is Finland's national beauty pageant academy, running the country's\npageant since 1931. The site needed to carry that history while still reading\nas current: pageant listings, media, a booking path and a newsletter signup,\nbuilt for a small team who publish new event pages themselves each season.",[119,709,451],{"id":450},[11,711,712],{},"A fully responsive site in WordPress with Elementor Pro, built from a supplied\ndesign: a custom homepage with a rotating booking banner, dedicated sections\nfor pageants, media and contact, a newsletter signup, and links out to\nexternal ticketing for live events. The Elementor sections are structured so\nthe client's own team can add a new pageant page or update a gallery without\ntouching code — the build optimizes for that handoff rather than for a\none-time launch.",[119,714,458],{"id":457},[11,716,717],{},"It's real, paid client work: a full site build, not a template, delivered to\na supplied design and still live and running the current pageant season.",{"title":91,"searchDepth":92,"depth":92,"links":719},[720,721,722],{"id":443,"depth":212,"text":444},{"id":450,"depth":212,"text":451},{"id":457,"depth":212,"text":458},{"src":724,"alt":725,"width":726,"height":727},"\u002Fimages\u002Fprojects\u002Fmiss-suomi-cover.webp","A laptop and phone showing the Miss Suomi Beauty Academy homepage, a winter portrait behind a booking prompt and a row of pageant event cards, beside the Miss Suomi crest logo and a \"Built with WordPress + Elementor\" badge",1672,940,[729,731,733,735],{"label":475,"value":730},"WordPress & Elementor developer",{"label":478,"value":732},"Finnartist Oy",{"label":481,"value":734},"Full site build, from supplied design",{"label":484,"value":736},"WordPress, Elementor Pro","Client project · WordPress & Elementor build","https:\u002F\u002Fmisssuomi.fi\u002F",{},[],"\u002Fprojects\u002Fmiss-suomi","WordPress & Elementor developer — design implementation, build, handoff","Beauty pageants & events",{"title":700,"description":91},[],[641,358,642],"projects\u002Fmiss-suomi","The website for Finland's national beauty pageant academy, running since 1931 — a fully responsive WordPress and Elementor Pro build the client's own team edits day to day.","2026","7qM35doG8jRkc-HAPl3fArJgTy81i3g6uNHAfprRuNE",{"id":752,"title":753,"body":754,"cover":951,"description":91,"eta":472,"extension":101,"facts":955,"featured":103,"featuredOrder":965,"kicker":966,"liveUrl":967,"meta":968,"metrics":969,"navigation":103,"order":92,"path":970,"placeholder":100,"repoUrl":472,"role":971,"sector":972,"seo":973,"shots":974,"stack":1012,"status":643,"stem":1015,"summary":1016,"type":524,"year":749,"__hash__":1017},"projects\u002Fprojects\u002Farvale.md","Arvale",{"type":8,"value":755,"toc":942},[756,758,761,764,768,771,777,783,793,797,800,867,871,878,882,930,934,937,939],[119,757,444],{"id":443},[11,759,760],{},"Arvale is a store I built for myself: an invented brand selling clothes, bags,\nfootwear and homeware \"made to be kept\", with a full catalogue, a journal and\nthe help pages a real shop needs. No client, so no brief to hide behind. The\ngoal was to show, in one live site, the kind of WooCommerce store clients\nusually ask me for, and how I build it.",[11,762,763],{},"The rule I set was that everything a premium store theme sells — colour\nswatches, AJAX filters, a mega menu, quick view, wishlist, compare, an\noff-canvas cart — had to be built on WooCommerce's own machinery, and every\npage had to stay editable in Elementor by someone who never opens the code.",[119,765,767],{"id":766},"how-its-put-together","How it's put together",[11,769,770],{},"Three layers, each with one job.",[11,772,773,776],{},[149,774,775],{},"A deliberately small child theme."," Arvale is a child of Hello Elementor. It\ncarries the brand stylesheet, the motion system and not much else. It doesn't\nhold templates or store logic.",[11,778,779,782],{},[149,780,781],{},"A custom plugin, Arvale Core, for everything the store does."," It's made of\n13 modules — swatches, filters, wishlist, compare, quick view, mini-cart,\nshipping, media, search, product page, icons, Elementor and demo mode. Each\none checks its own dependencies and can be switched off from a settings page,\nand each loads its CSS and JS only on pages where it actually renders. It\ndeclares compatibility with WooCommerce's HPOS order tables.",[11,784,785,788,789,792],{},[149,786,787],{},"Elementor Pro for every layout."," Header, footer, the product card, the shop\narchive, the single product page, the journal, the 404 and the newsletter\npopup are all Theme Builder templates. The plugin adds its own ",[140,790,791],{},"Arvale Store","\npanel to the editor: 18 custom widgets (mega menu, category index, shop the\nlook, product media, product filters, product gallery, reviews, key details\nand more) and 4 dynamic tags (sale percentage, stock status, \"spend $X more for\nfree shipping\", category count), so the store-specific pieces drop into a\nlayout like any native widget.",[119,794,796],{"id":795},"built-on-woocommerce-not-around-it","Built on WooCommerce, not around it",[11,798,799],{},"This is the part I care about most, because it's where store builds usually\nbreak on the next WooCommerce update.",[45,801,802,820,830,839,845,851,861],{},[48,803,804,807,808,811,812,815,816,819],{},[149,805,806],{},"Filters speak WooCommerce's own URL language"," (",[160,809,810],{},"filter_color",",\n",[160,813,814],{},"min_price",", ",[160,817,818],{},"rating_filter","), so the main shop query does the filtering and\nevery filtered view is a real, shareable, crawlable URL. JavaScript only\nswaps the results in place. Each facet's counts ignore its own selection,\nso choosing Black never hides Navy from the colour list, and the counts are\ncached against WooCommerce's own product version.",[48,821,822,825,826,829],{},[149,823,824],{},"Swatches replace variation dropdowns with accessible buttons",", but the\nnative ",[160,827,828],{},"\u003Cselect>"," stays in the form, so WooCommerce's variation script still\nhandles matching, price, stock and image changes.",[48,831,832,838],{},[149,833,834,835],{},"The off-canvas cart goes through ",[160,836,837],{},"WC()->cart"," and arrives as a standard\ncart fragment, with quantity steppers and a free-shipping progress bar.",[48,840,841,844],{},[149,842,843],{},"Quick view renders WooCommerce's own single-product functions",", so price,\nvariations and add to cart behave exactly as they do on the product page.",[48,846,847,850],{},[149,848,849],{},"Live search is a normal product search form underneath."," Press Enter, or\nturn JavaScript off, and you land on WooCommerce's search results.",[48,852,853,856,857,860],{},[149,854,855],{},"The wishlist"," lives in user meta for customers and a cookie for guests,\nmerged at login. ",[149,858,859],{},"Compare"," (up to four products) lives in the browser,\nso both keep working on fully cached pages.",[48,862,863,866],{},[149,864,865],{},"The product page"," adds badges, extra data tabs and a sticky add-to-cart\nbar that drives the page's real cart form, not a copy of it.",[119,868,870],{"id":869},"reproducible-not-hand-clicked","Reproducible, not hand-clicked",[11,872,873,874,877],{},"The whole store can be rebuilt from a WP-CLI command. ",[160,875,876],{},"wp arvale all","\nconfigures the store the way a merchant would (currency, tax, shipping zones,\npayment methods, attributes, brands, coupons) and generates the 300-product\ncatalogue through WooCommerce's CRUD classes, not raw SQL, so lookup tables,\nprice caches and variation sync all behave as if someone had typed the\nproducts in. It then imports the photography by folder, adds reviews, and\nwrites the design into Elementor: the kit's colours and type, the Theme\nBuilder templates, the product card and every page. It saves all of that\nthrough Elementor's Document API, so the result can't be told apart from work\ndone in the editor, and it's safe to run again.",[119,879,881],{"id":880},"details-that-make-it-feel-finished","Details that make it feel finished",[45,883,884,890,896,902,908,914],{},[48,885,886,889],{},[149,887,888],{},"A mega menu"," built from an ordinary WordPress menu. Product categories\npick up their counts and thumbnails automatically, and below 1025px the\nmenu becomes a drawer with the same structure as accordions.",[48,891,892,895],{},[149,893,894],{},"A typographic category index"," on the homepage with live product counts\nand an image preview that follows the pointer. Keyboard and reduced-motion\nusers get the image beside the row instead.",[48,897,898,901],{},[149,899,900],{},"Shop the look",": an editorial photo with hotspots that open live product\ncards, with price, stock and add to cart.",[48,903,904,907],{},[149,905,906],{},"Its own line-icon set",", registered as a library in Elementor's icon picker\nso every icon control can use it.",[48,909,910,913],{},[149,911,912],{},"One motion system",": sections that rise into view, headlines revealed line\nby line, image wipes, a marquee, and a header that hides on scroll down. It\nnever hides content when JavaScript fails, and none of it runs for people\nwho prefer reduced motion.",[48,915,916,811,919,924,925,929],{},[149,917,918],{},"The contact form and dropdowns run on my own plugins",[920,921,923],"a",{"href":922},"\u002Fprojects\u002Fdefer-forms-for-contact-form-7","Defer Forms for Contact Form 7","\nand ",[920,926,928],{"href":927},"\u002Fprojects\u002Fnice-select-for-wp","Nice Select for WP",".",[119,931,933],{"id":932},"tuned-for-free-hosting","Tuned for free hosting",[11,935,936],{},"It runs on free shared hosting, and that shaped a few decisions. The media module stops WordPress generating image sizes the\ntheme never uses and saves the ones it does need as WebP, which also matters\non hosts that cap the number of files you can store. WooCommerce's zoom,\nslider and lightbox scripts never load, because the product gallery is a native\nscroll-snap slider. Block editor styles load only on journal posts. A demo-mode\nmodule offers only offline payment methods, shows a quiet notice at checkout\nand tags every order, so anyone can walk through a full checkout and nothing\nis ever charged.",[119,938,458],{"id":457},[11,940,941],{},"Client sites belong to the client, and they change after handover. Arvale is\na store I control end to end, so it shows how I build WooCommerce sites when\nnothing is decided for me: Elementor Pro handles the layout so a client's own\nteam can edit it, and one clean, modular plugin handles the store logic\nwithout fighting WooCommerce, so it keeps working after the next update.",{"title":91,"searchDepth":92,"depth":92,"links":943},[944,945,946,947,948,949,950],{"id":443,"depth":212,"text":444},{"id":766,"depth":212,"text":767},{"id":795,"depth":212,"text":796},{"id":869,"depth":212,"text":870},{"id":880,"depth":212,"text":881},{"id":932,"depth":212,"text":933},{"id":457,"depth":212,"text":458},{"src":952,"alt":953,"width":726,"height":954},"\u002Fimages\u002Fprojects\u002Farvale-cover.webp","A laptop and phone showing the Arvale homepage, the headline \"Clothes, bags and objects made to be kept\" beside a fashion photo above a large typographic category index, next to the Arvale logo and the labels Responsive Design, Custom Elementor Build and WooCommerce",941,[956,958,961,964],{"label":475,"value":957},"Design, development, content",{"label":959,"value":960},"Catalogue","300 products · 6 departments · 27 sub-categories",{"label":962,"value":963},"Custom code","One plugin, 13 modules, 18 Elementor widgets",{"label":484,"value":577},0.5,"Own build · Elementor Pro & WooCommerce store","https:\u002F\u002Farvale.freedev.app\u002F",{},[],"\u002Fprojects\u002Farvale","Designer & WordPress developer — design, custom plugin, child theme, Elementor build","Fashion & lifestyle e-commerce",{"title":753,"description":91},[975,979,984,989,994,999,1003,1007],{"src":976,"alt":977,"width":498,"height":978,"caption":500},"\u002Fimages\u002Fprojects\u002Farvale-home.webp","The Arvale homepage, with a split hero, a strip of service promises, a large typographic Shop by category index, a New this month carousel, a Shop the look photo with product hotspots, a Most loved product grid, a scrolling marquee, a dark Made in small runs section and a From the journal feature above the footer",4929,{"src":980,"alt":981,"width":498,"height":982,"caption":983},"\u002Fimages\u002Fprojects\u002Farvale-category.webp","The Lighting category page, with a sidebar of filters for category, price range, material, rating and availability beside a three-column grid of lamps, each card with a photo, badges such as New, Low stock or a sale percentage, a wishlist heart, name and price",2279,"Category — AJAX filters and product cards",{"src":985,"alt":986,"width":498,"height":987,"caption":988},"\u002Fimages\u002Fprojects\u002Farvale-product.webp","The Soft Recycled Nylon Overcoat product page, with a thumbnail rail beside the main photo, colour swatches and size buttons, quantity, add to cart, wishlist and compare, delivery promises and accordions for description, specifications, materials and shipping, then a reviews section with a rating breakdown, a You may also like carousel and a sticky add-to-cart bar along the bottom",2868,"Product — swatches, gallery, reviews, sticky add to cart",{"src":990,"alt":991,"width":498,"height":992,"caption":993},"\u002Fimages\u002Fprojects\u002Farvale-journal.webp","The journal archive, titled Notes from the studio, with one large lead story beside two smaller ones and a row of three more stories below",1715,"Journal",{"src":995,"alt":996,"width":498,"height":997,"caption":998},"\u002Fimages\u002Fprojects\u002Farvale-article.webp","A journal article, Inside the kiln, with a wide photograph of a ceramics studio's shelves, a short body with a subheading, share buttons, a previous-story link and a More from the journal row",2017,"Journal article",{"src":1000,"alt":1001,"width":498,"height":1002,"caption":639},"\u002Fimages\u002Fprojects\u002Farvale-about.webp","The About page, with the headline Fewer, better things, made to be kept, a wide editorial photo, a Why we started story, a dark What we hold to section with three principle cards, a row of four counters, a pull quote and a closing call to action",2941,{"src":1004,"alt":1005,"width":498,"height":569,"caption":1006},"\u002Fimages\u002Fprojects\u002Farvale-size-guide.webp","The size guide, with Clothing and Footwear tabs above a table of chest, waist and hip measurements from XS to XL, beside a How to measure panel","Size guide",{"src":1008,"alt":1009,"width":498,"height":1010,"caption":1011},"\u002Fimages\u002Fprojects\u002Farvale-contact.webp","The contact page, with email, studio location, hours and a before-you-write note on the left and a Send a message form on the right",1186,"Contact",[641,642,362,1013,1014],"PHP","WP-CLI","projects\u002Farvale","A fashion and homeware store I designed and built end to end — Elementor Pro for every layout, WooCommerce for the shop, and a custom plugin that adds the features premium store themes sell, on top of WooCommerce rather than around it.","MuvVFMYInMKs2NhzP9bolBINWXEgQNw2GBzHIFz7g2E",{"id":1019,"title":923,"body":1020,"cover":1155,"description":91,"eta":472,"extension":101,"facts":1158,"featured":103,"featuredOrder":472,"kicker":1170,"liveUrl":1171,"meta":1172,"metrics":1173,"navigation":103,"order":287,"path":922,"placeholder":100,"repoUrl":1183,"role":1184,"sector":1185,"seo":1186,"shots":1187,"stack":1196,"status":643,"stem":1200,"summary":1201,"type":1202,"year":749,"__hash__":1203},"projects\u002Fprojects\u002Fdefer-forms-for-contact-form-7.md",{"type":8,"value":1021,"toc":1145},[1022,1024,1027,1030,1034,1037,1040,1044,1047,1050,1054,1063,1069,1075,1081,1085,1088,1092,1112,1116,1119,1122,1126,1129,1132,1135,1138,1142],[119,1023,444],{"id":443},[11,1025,1026],{},"Contact Form 7 is one of the most installed plugins in WordPress, and it is\nconfigured by typing bracket tags into a textarea. That is fine for a developer\nbuilding one form. It is not fine for the person who owns the site afterwards,\nand that is the person who needs to change the form six months later.",[11,1028,1029],{},"I know the shape of this problem from the other end. Every handover I have done\nended the same way: a document explaining how to edit the form, then an email\nthree weeks later asking me to edit the form.",[119,1031,1033],{"id":1032},"the-problem","The problem",[11,1035,1036],{},"Rewriting the plugin was never an option. Contact Form 7 sits under an enormous\nnumber of live sites, including many I did not build, and any tool that asks an\nowner to migrate their forms has already failed — the forms are wired into mail\ntemplates, spam filtering, third-party integrations and, on some sites, years of\nhabit.",[11,1038,1039],{},"So the constraint was set before the first line: a form that exists today must\nkeep working exactly as it does, untouched, whether or not anyone ever opens the\nnew interface.",[119,1041,1043],{"id":1042},"approach","Approach",[11,1045,1046],{},"The plugin is a layer, not a replacement. Contact Form 7 remains the thing that\nrenders, validates and mails the form. Defer Forms owns the\nediting experience and compiles what you build into the tag syntax CF7 already\nunderstands, so the generated output is something the plugin — and any developer\nreading it — treats as ordinary.",[11,1048,1049],{},"That decision paid for itself repeatedly. Anything CF7 can do, an escape hatch\ncan still do; anything CF7 cannot do stays out of scope instead of turning into\na fork I would have to maintain against every upstream release.",[119,1051,1053],{"id":1052},"technical-decisions","Technical decisions",[11,1055,1056,1059,1060,1062],{},[149,1057,1058],{},"A React canvas, mounted inside the WordPress admin."," The builder is a real\napplication — drag and drop, selection, undo, a properties panel — and pretending\notherwise with jQuery would have cost more than it saved. ",[160,1061,347],{}," handles the\ndragging, because it is keyboard-operable and accessible by default rather than\nby later effort.",[11,1064,1065,1068],{},[149,1066,1067],{},"The REST API as the seam."," The builder talks to the plugin over REST routes\nand nothing else. The PHP side has no idea a React application exists, which is\nwhat makes the PHP side testable on its own.",[11,1070,1071,1074],{},[149,1072,1073],{},"Submissions as a database table, not post meta."," Form entries are stored as\nrecords, with the field mapping kept alongside them, so a submission stays\nreadable after the form it came from has been edited.",[11,1076,1077,1080],{},[149,1078,1079],{},"A hooks API before a settings page."," Seven documented hooks sit on a\nsubmission — keep a form out of the table, change what is stored before it is\nwritten, act on an entry once it exists — and they are the plugin's public\nsurface. They are documented in the readme the plugin ships with rather than in\na markdown file beside it, because a reference belongs where somebody looking\nfor it would actually go: the plugin's own page.",[119,1082,1084],{"id":1083},"implementation","Implementation",[11,1086,1087],{},"Seventeen features ship in the free plugin. The ones that changed the shape of\nthe codebase most:",[1089,1090],"stat-row",{":items":1091},"[{\"value\":\"~12,400\",\"label\":\"lines of PHP\"},{\"value\":\"22\",\"label\":\"JS and JSX source files\"},{\"value\":\"112\",\"label\":\"test suites, all passing\"}]",[45,1093,1094,1097,1100,1103,1106,1109],{},[48,1095,1096],{},"A submissions database with export, so a client can read what people sent\nwithout opening their inbox.",[48,1098,1099],{},"Multi-step forms, which required the compiler to think about state that\nContact Form 7 has no concept of.",[48,1101,1102],{},"Conditional logic, edited as rules and compiled to markup and behaviour.",[48,1104,1105],{},"Revisions, because a form is content and content gets broken by whoever edited\nit last.",[48,1107,1108],{},"GDPR export and erase, wired into WordPress's own personal-data tools rather\nthan a private screen of my own.",[48,1110,1111],{},"Notifications to Telegram, Slack and Discord for the clients who do not read\nemail.",[119,1113,1115],{"id":1114},"challenges","Challenges",[11,1117,1118],{},"The hardest part was not the builder. It was compilation fidelity: taking a\nvisual structure and producing tag syntax that CF7 parses into precisely the\nform the user drew, including the cases where the visual model is richer than\nthe syntax. Where the two could not be reconciled, the builder refuses to\nproduce something it cannot round-trip rather than quietly producing something\nclose.",[11,1120,1121],{},"The second was scope. A form builder attracts feature requests indefinitely, and\nthe answer to most of them is the hooks API rather than another checkbox in a\nsettings page.",[119,1123,1125],{"id":1124},"outcome","Outcome",[11,1127,1128],{},"The plugin is in the WordPress plugin directory at v2.7.1, requiring WordPress\n6.5 and PHP 8.0, with a documented hooks API, a guarded release script, and a\nmaintainer document written in Bengali so the person maintaining it in my own\nlanguage can do so without translating my code comments first.",[11,1130,1131],{},"It is published from the account of Defer Studio, the agency I am setting up,\nwhich is why WordPress.org lists that account as the plugin's owner and me as a\ncontributor. The code, the builder and the release are all mine.",[11,1133,1134],{},"The directory review is where the current name came from: the previous one led\nwith a generic word and sat too close to an existing plugin's. Renaming a\nplugin is mostly unglamorous work — an internal prefix, a database table, a\nREST namespace, every admin address — and it is worth recording that the\nawkward part of shipping is rarely the part that was interesting to build.",[11,1136,1137],{},"It went public in September 2026, so the install count is still in single\nfigures and I am not going to pretend otherwise. What is genuinely finished is\nthe thing that mattered at the start: existing forms do not have to change.",[119,1139,1141],{"id":1140},"what-i-would-take-to-the-next-one","What I would take to the next one",[11,1143,1144],{},"The escape hatch is why this works. Every tool that sits on top of an\nestablished plugin is really a bet that you can improve the interface without\ninheriting the maintenance of everything underneath it — and the bet only pays\nif the user can always drop back down to the layer below.",{"title":91,"searchDepth":92,"depth":92,"links":1146},[1147,1148,1149,1150,1151,1152,1153,1154],{"id":443,"depth":212,"text":444},{"id":1032,"depth":212,"text":1033},{"id":1042,"depth":212,"text":1043},{"id":1052,"depth":212,"text":1053},{"id":1083,"depth":212,"text":1084},{"id":1114,"depth":212,"text":1115},{"id":1124,"depth":212,"text":1125},{"id":1140,"depth":212,"text":1141},{"src":1156,"alt":1157,"width":97,"height":676},"\u002Fimages\u002Fprojects\u002Fdefer-forms-cover.webp","The Defer Forms for Contact Form 7 product banner: the plugin name beside a mockup of the builder, with a field palette on the left, a text field being dragged onto the form canvas, and panels for notifications and stored submissions alongside",[1159,1161,1164,1167],{"label":475,"value":1160},"Solo developer",{"label":1162,"value":1163},"Release","v2.7.1 on WordPress.org",{"label":1165,"value":1166},"Codebase","~12,400 lines of PHP",{"label":1168,"value":1169},"Published by","Defer Studio, my agency","WordPress plugin","https:\u002F\u002Fwordpress.org\u002Fplugins\u002Fdefer-forms-for-contact-form-7\u002F",{},[1174,1177,1180],{"value":1175,"label":1176},"0","existing forms that have to change",{"value":1178,"label":1179},"17","features shipped in the free plugin",{"value":1181,"label":1182},"112","test suites, all passing","https:\u002F\u002Fgithub.com\u002FRupashdas\u002Fdefer-forms-for-contact-form-7","Solo — architecture, plugin, builder UI, tests, release","WordPress products",{"title":923,"description":91},[1188,1192],{"src":1189,"alt":1190,"width":97,"height":676,"caption":1191},"\u002Fimages\u002Fprojects\u002Fdefer-forms-01.webp","A field's settings panel open in the builder, showing the label, field name and placeholder for an email field","The canvas. Every field is a block, and the shortcode is generated underneath rather than typed.",{"src":1193,"alt":1194,"width":97,"height":676,"caption":1195},"\u002Fimages\u002Fprojects\u002Fdefer-forms-02.webp","The conditional logic editor, showing an AND rule built from two field conditions with a value comparison","Conditional rules are edited as rules and compiled down to Contact Form 7's own tag syntax.",[641,1197,1198,347,1199,275,304],"PHP 8.2","React","WordPress REST API","projects\u002Fdefer-forms-for-contact-form-7","A drag-and-drop builder for Contact Form 7 — canvas, conditional logic and a submissions database — built as a layer that changes nothing about the forms already on a site.","plugin","uedv1tE-OkEOk6cXdx07OclnV4cM7EHA-MU481LciVc",{"id":1205,"title":928,"body":1206,"cover":1273,"description":91,"eta":472,"extension":101,"facts":1276,"featured":100,"featuredOrder":472,"kicker":1170,"liveUrl":1285,"meta":1286,"metrics":1287,"navigation":103,"order":391,"path":927,"placeholder":100,"repoUrl":1297,"role":1298,"sector":1185,"seo":1299,"shots":1300,"stack":1301,"status":643,"stem":1303,"summary":1304,"type":1202,"year":1305,"__hash__":1306},"projects\u002Fprojects\u002Fnice-select-for-wp.md",{"type":8,"value":1207,"toc":1267},[1208,1210,1219,1221,1228,1235,1237,1243,1256,1262,1264],[119,1209,444],{"id":443},[11,1211,1212,1213,1215,1216,1218],{},"A native HTML ",[160,1214,828],{}," is the one form control browsers never got around to\nmaking presentable, and WordPress ships it as-is everywhere: contact forms,\ncheckout fields, filters. Nice Select for WP is the plugin I built to fix that\nwithout asking anyone to write CSS — activate it, and every ",[160,1217,828],{}," on the\nsite picks up a styled, searchable dropdown from a single settings page.",[119,1220,1033],{"id":1032},[11,1222,1223,1224,1227],{},"Multi-select was the part that actually needed solving. A ",[160,1225,1226],{},"\u003Cselect multiple>","\nis worse than a single dropdown — no visible list of what's chosen, no way to\nremove one item without hunting for it in a scroll box — and most \"nicer\nselect\" plugins simply don't touch it. Ignoring it would have left the plugin\nsolving the easy half of the problem.",[11,1229,1230,1231,1234],{},"The other constraint was WordPress itself: forms get added after the page\nhas already loaded — AJAX content, popups, a form plugin's own conditional\nlogic revealing a field — and a plugin that only enhances what's present at\n",[160,1232,1233],{},"DOMContentLoaded"," quietly fails on exactly the forms most likely to need it.",[119,1236,1043],{"id":1042},[11,1238,1239,1242],{},[149,1240,1241],{},"Settings, not markup."," Every option — which selector to target, alignment,\nfull width, placeholder text, search, custom CSS — lives on one admin page.\nNothing about using the plugin requires editing a template or writing a\nshortcode.",[11,1244,1245,1248,1249,1251,1252,1255],{},[149,1246,1247],{},"Multi-select as a real control, not a styled list."," ",[160,1250,1226],{},"\nrenders as a tag-based UI: selected options become removable tags, ",[160,1253,1254],{},"\u003Coptgroup>","\nheaders are preserved, and Select All \u002F Clear All, close-after-pick, and a\nmaximum-selection cap are each their own setting rather than always-on\nbehaviour.",[11,1257,1258,1261],{},[149,1259,1260],{},"Watch the DOM, not just the load event."," Selects added or revealed after\nthe page loads get enhanced automatically, which is what makes the plugin\nwork inside Contact Form 7's conditional fields and Gravity Forms' AJAX\nsteps instead of only on the fields present when the page first rendered.",[119,1263,1125],{"id":1124},[11,1265,1266],{},"Eight releases since the 1.0.0 launch, each logged in the changelog rather\nthan folded silently into the next version — including two releases that\nexist only to fix bugs real usage surfaced: a placeholder option that could\nfail a form plugin's server-side validation, and settings that weren't being\ncleaned up on uninstall. It's public on WordPress.org and GitHub, GPLv2 or\nlater, with 20+ active installs and no code required on the side of anyone\nrunning it.",{"title":91,"searchDepth":92,"depth":92,"links":1268},[1269,1270,1271,1272],{"id":443,"depth":212,"text":444},{"id":1032,"depth":212,"text":1033},{"id":1042,"depth":212,"text":1043},{"id":1124,"depth":212,"text":1125},{"src":1274,"alt":1275,"width":97,"height":676},"\u002Fimages\u002Fprojects\u002Fnice-select-for-wp-cover.webp","The Nice Select for WP promotional graphic: a default browser select box open on a fruit list, beside the plugin's searchable multi-select showing three chosen fruits as removable tags above a search field and checkmarked list, under the headline \"Beautiful, flexible and easy-to-use select dropdowns for WordPress.\"",[1277,1278,1280,1282],{"label":475,"value":1160},{"label":1162,"value":1279},"v1.2.3, on WordPress.org and GitHub",{"label":1165,"value":1281},"~1,150 lines of PHP, JS and CSS",{"label":1283,"value":1284},"License","GPLv2 or later","https:\u002F\u002Fwordpress.org\u002Fplugins\u002Fnice-select-for-wp\u002F",{},[1288,1291,1294],{"value":1289,"label":1290},"6","settings, no code required",{"value":1292,"label":1293},"8","releases shipped since 1.0.0",{"value":1295,"label":1296},"20+","active installs on WordPress.org","https:\u002F\u002Fgithub.com\u002FRupashdas\u002Fnice-select-for-wp","Solo — plugin, settings UI, release",{"title":928,"description":91},[],[641,1013,519,1302],"CSS","projects\u002Fnice-select-for-wp","A settings-driven plugin that restyles every \u003Cselect> on a WordPress site — including full multi-select with removable tags — without the site owner touching a line of code.","2024","Yrjuy969Rq3trfJU4wCC2-MzImh8yo860OEbacHx39E",{"id":1308,"title":1309,"body":1310,"cover":1559,"description":91,"eta":472,"extension":101,"facts":1562,"featured":103,"featuredOrder":472,"kicker":1572,"liveUrl":1321,"meta":1573,"metrics":1574,"navigation":103,"order":223,"path":1583,"placeholder":100,"repoUrl":1584,"role":1585,"sector":1586,"seo":1587,"shots":1588,"stack":1602,"status":521,"stem":1609,"summary":1610,"type":1611,"year":749,"__hash__":1612},"projects\u002Fprojects\u002Finventory-pos.md","Shopfront POS",{"type":8,"value":1311,"toc":1550},[1312,1326,1330,1333,1336,1340,1347,1350,1361,1364,1367,1370,1374,1394,1397,1407,1429,1433,1443,1453,1465,1468,1472,1475,1527,1531,1534,1537,1540,1544,1547],[1313,1314,1316],"callout",{"type":1315},"note",[11,1317,1318,1319,1325],{},"A live demo runs on seeded data at\n",[920,1320,1324],{"href":1321,"rel":1322},"https:\u002F\u002Fshopfront.freedev.app\u002F",[1323],"nofollow","shopfront.freedev.app","; the sign-in page fills\nin a demo account for you. The screenshots are the real application. There are\nno usage numbers here because there are no real shops using it yet.",[119,1327,1329],{"id":1328},"what-it-is","What it is",[11,1331,1332],{},"A point-of-sale application for a small shop: a till, a catalogue, customers,\ninvoices, and stock. One account is one shop, and every query is scoped to its\nowner.",[11,1334,1335],{},"I had written most of it earlier as a way to learn Laravel properly. Coming back\nto it to put it in this portfolio, I read it as if someone else had sent it to\nme — and found enough wrong that the honest thing to write up is the audit\nrather than the feature list.",[119,1337,1339],{"id":1338},"the-thing-it-was-named-for-and-did-not-have","The thing it was named for and did not have",[11,1341,1342,1343,1346],{},"The repository is called ",[140,1344,1345],{},"Inventory Management System",". There was no inventory.",[11,1348,1349],{},"A sale wrote an invoice row and left the catalogue completely untouched. Nothing\nin the schema could answer \"how many are left\", so nothing could stop you selling\nthe twentieth of three, and there was no page anywhere that would tell a\nshopkeeper to reorder.",[11,1351,1352,1353,1356,1357,1360],{},"Stock now exists in two pieces, because one is not enough. ",[160,1354,1355],{},"products.stock"," is\nthe balance. ",[160,1358,1359],{},"stock_movements"," is how the balance came to be — a signed row for\nevery change, with the reason and the resulting total:",[1089,1362],{":items":1363},"[{\"value\":\"4\",\"label\":\"movement reasons\"},{\"value\":\"1\",\"label\":\"row per stock change\"},{\"value\":\"0\",\"label\":\"ways to edit the balance directly\"}]",[11,1365,1366],{},"The last of those is the decision I would defend hardest. Stock is deliberately\nnot a field on the product form. It moves through restocking and through sales,\nboth of which write a ledger row. Letting a form overwrite the balance would put\nthe number and its history permanently out of step, and then the ledger is\ndecoration.",[11,1368,1369],{},"Selling takes a row lock while it checks and decrements, so two tills reaching\nfor the last unit at the same moment cannot both read \"1 left\" and both succeed.\nDeleting an invoice puts everything on it back and records that as a reversal\nrather than quietly editing the earlier rows.",[119,1371,1373],{"id":1372},"three-holes-all-the-same-mistake","Three holes, all the same mistake",[11,1375,1376,1248,1379,1382,1383,815,1386,1389,1390,1393],{},[149,1377,1378],{},"The server believed the browser.",[160,1380,1381],{},"invoiceCreate"," read ",[160,1384,1385],{},"total",[160,1387,1388],{},"vat"," and\n",[160,1391,1392],{},"payable"," straight out of the request body and wrote them to the invoice. The\nbrowser was not merely displaying the price, it was setting it — a hand-written\nPOST could buy a desk lamp for a dollar.",[11,1395,1396],{},"The request now carries only what the browser is entitled to decide: which\nproducts, how many, which customer, what discount was agreed. Every figure is\ncomputed server-side from the catalogue. I found this one properly by accident,\nwhile testing: I fed the endpoint a line the page had priced at $1,450 and the\nserver charged $765.45, because $1,450 was never a real price for that product.",[11,1398,1399,1402,1403,1406],{},[149,1400,1401],{},"Deleting an invoice needed only its number."," The child rows were scoped to\nthe owner; the parent was not. ",[160,1404,1405],{},"Invoice::where('id', $inv_id)->delete()",". Any\nsigned-in user could delete anybody's sale by guessing an integer. The ownership\ncheck had been written — it just was not applied where it mattered, which is the\nversion of this bug that survives review.",[11,1408,1409,1412,1413,1416,1417,1420,1421,1424,1425,1428],{},[149,1410,1411],{},"The request chose which file to delete."," Product delete and update read a\n",[160,1414,1415],{},"file_path"," field out of the POST body and handed it to ",[160,1418,1419],{},"File::delete()",". The\npath now comes from the product row, and the resolved path is confirmed to sit\ninside ",[160,1422,1423],{},"public\u002Fuploads"," before anything is removed. Uploaded images get a\ngenerated filename too — the original was pasting ",[160,1426,1427],{},"getClientOriginalName()","\nstraight into the path.",[119,1430,1432],{"id":1431},"the-bug-that-only-mysql-could-see","The bug that only MySQL could see",[11,1434,1435,1438,1439,1442],{},[160,1436,1437],{},"password"," was ",[160,1440,1441],{},"VARCHAR(50)",". A bcrypt hash is sixty characters.",[11,1444,1445,1446,1449,1450,929],{},"It had never failed, because the project was built against SQLite, which does not\nenforce ",[160,1447,1448],{},"VARCHAR"," lengths. The column was wrong from the first migration and the\ndevelopment database politely agreed with it. Moving to MySQL, every single\nregistration died on ",[160,1451,1452],{},"Data too long for column 'password'",[11,1454,1455,1456,815,1459,811,1462,929],{},"That is why the test suite runs on MySQL and not on an in-memory SQLite\ndatabase. A suite on SQLite would have agreed with the bug rather than caught it,\nand the queries here are MySQL's anyway — ",[160,1457,1458],{},"DATE()",[160,1460,1461],{},"CAST(x AS UNSIGNED)",[160,1463,1464],{},"SELECT … FOR UPDATE",[1089,1466],{":items":1467},"[{\"value\":\"27\",\"label\":\"feature tests\"},{\"value\":\"93\",\"label\":\"assertions\"},{\"value\":\"0\",\"label\":\"failures\"}]",[119,1469,1471],{"id":1470},"smaller-things-found-by-reading","Smaller things, found by reading",[11,1473,1474],{},"Not everything was structural. These are the ones worth listing because each is\na different way for code to look fine and not work:",[45,1476,1477,1488,1499,1506,1509],{},[48,1478,1479,1480,1483,1484,1487],{},"The sales report passed ",[160,1481,1482],{},"FormDate"," as ",[149,1485,1486],{},"both"," ends of its range, so every PDF\nclaimed to cover a single day whatever you asked for.",[48,1489,1490,1491,1494,1495,1498],{},"The reports page had its ",[160,1492,1493],{},"\u003Cscript>"," after ",[160,1496,1497],{},"@endsection",". Blade discards\nanything a child template puts outside a section, so the Download button\ncalled a function that never reached the page at all.",[48,1500,1501,1502,1505],{},"Registration validated the form, showed a message for the empty field, and\nthen fell straight through and posted anyway — no ",[160,1503,1504],{},"return"," after the warning.",[48,1507,1508],{},"The OTP and password-reset forms caught every error into an empty block. A\nwrong code left the loading bar running and said nothing.",[48,1510,1511,1514,1515,1518,1519,1522,1523,1526],{},[160,1512,1513],{},"ResetPass()"," called ",[160,1516,1517],{},"event.preventDefault()"," without declaring ",[160,1520,1521],{},"event",". That\nworks in Chrome, which still exposes ",[160,1524,1525],{},"window.event",", and does not in Firefox,\nwhere the form did a full page submit instead.",[119,1528,1530],{"id":1529},"the-interface","The interface",[11,1532,1533],{},"The application worked before this and looked like a template. I rebuilt the\nfront end on a small token-based stylesheet — one accent, three stock colours,\none radius, one shadow — over the Bootstrap that was already there.",[11,1535,1536],{},"The accent is indigo rather than the magenta it started with, chosen partly\nbecause it sits far enough from green, amber and red that a primary button and\nan \"in stock\" badge can never be confused. That matters more here than it sounds:\ncolour is doing real work on this screen.",[11,1538,1539],{},"The dashboard was seven identical counters. It is now ordered by the question\nbeing asked — money taken today first, catalogue counts last — with a takings\nchart, a reorder list, best sellers and recent sales. The till became three\ncolumns in the order the sale actually happens. Every error message names the\nproduct and the number left, because \"something went wrong\" tells a cashier with\na queue in front of them nothing they can act on.",[119,1541,1543],{"id":1542},"what-is-honestly-not-done","What is honestly not done",[11,1545,1546],{},"It runs as a public demo, not in a real shop, so it has no users and no outcome\nsection. Payment is a\ntotal on a screen, not a card reader. There is one role — the shop owner — with\nno staff accounts or permissions, which is the next real piece of work and a\nmuch larger one than it looks. Sales tax is a single fixed rate.",[11,1548,1549],{},"What the project produced is the thing I wanted from it: reading my own Laravel\ncode carefully enough to find three ways it could be abused, and being able to\nsay exactly why each one was wrong.",{"title":91,"searchDepth":92,"depth":92,"links":1551},[1552,1553,1554,1555,1556,1557,1558],{"id":1328,"depth":212,"text":1329},{"id":1338,"depth":212,"text":1339},{"id":1372,"depth":212,"text":1373},{"id":1431,"depth":212,"text":1432},{"id":1470,"depth":212,"text":1471},{"id":1529,"depth":212,"text":1530},{"id":1542,"depth":212,"text":1543},{"src":1560,"alt":1561,"width":97,"height":676},"\u002Fimages\u002Fprojects\u002Finventory-pos-cover.svg","The words \"Shopfront POS\" set on pale slate, beside four shelf columns standing in for stock levels, the third nearly empty and lit in indigo",[1563,1564,1567,1569],{"label":475,"value":1160},{"label":1565,"value":1566},"Architecture","Laravel 10 + Blade, JSON API",{"label":481,"value":1568},"7 models, 11 migrations, 32 routes",{"label":1570,"value":1571},"Tests","27 feature tests on MySQL","Point of sale and stock control",{},[1575,1578,1580],{"value":1576,"label":1577},"3","security holes found and closed",{"value":1175,"label":1579},"stock tracking before this pass",{"value":1581,"label":1582},"93","assertions, all passing","\u002Fprojects\u002Finventory-pos","https:\u002F\u002Fgithub.com\u002FRupashdas\u002FInventory-Management-System---POS","Solo — data model, API, stock ledger, interface, tests","Web applications",{"title":1309,"description":91},[1589,1594,1598],{"src":1590,"alt":1591,"width":97,"height":1592,"caption":1593},"\u002Fimages\u002Fprojects\u002Finventory-pos-02.webp","The till screen with three items on a sale for Sarah Mitchell, a five percent discount, and a payable total of 57 dollars 58",723,"The till. The shelf shows what is left after the basket, not before it, and the totals on the left are a preview — the server recomputes every figure from the catalogue when Confirm is pressed.",{"src":1595,"alt":1596,"width":97,"height":1592,"caption":1597},"\u002Fimages\u002Fprojects\u002Finventory-pos-01.webp","The dashboard showing takings today, a fourteen-day sales chart, a needs-reordering panel with two products out of stock, best sellers and recent invoices","The dashboard answers the two questions a shop actually opens it for — what came in today, and what is about to run out. The amber and red tiles only colour when there is something wrong.",{"src":1599,"alt":1600,"width":97,"height":1592,"caption":1601},"\u002Fimages\u002Fprojects\u002Finventory-pos-03.webp","The products and stock table, each row showing a stock badge, retail value, and buttons to restock, view history, edit and delete","Stock is a badge with three states, not a number. Out of stock is its own state because a product with none left cannot be sold at all, while a low one still can.",[1603,304,1604,1605,1606,1607,1608],"Laravel 10","Blade","JWT","DomPDF","Chart.js","PHPUnit","projects\u002Finventory-pos","A till and inventory application in Laravel — and an audit of my own code, which turned out to be selling stock it did not have and letting the browser decide what things cost.","application","G5eGzsDXiFUXCwQcKZPOaGYA1EDpx6L8clvwPHN2Nwk",{"id":1614,"title":1615,"body":1616,"cover":1638,"description":91,"eta":472,"extension":101,"facts":1641,"featured":100,"featuredOrder":472,"kicker":486,"liveUrl":472,"meta":1648,"metrics":1649,"navigation":103,"order":1650,"path":1651,"placeholder":100,"repoUrl":472,"role":491,"sector":1652,"seo":1653,"shots":1654,"stack":1655,"status":521,"stem":1656,"summary":1657,"type":524,"year":525,"__hash__":1658},"projects\u002Fprojects\u002Fapplause.md","Applause",{"type":8,"value":1617,"toc":1633},[1618,1620,1623,1625,1628,1630],[119,1619,444],{"id":443},[11,1621,1622],{},"Applause is a Slovenian, EU-funded project that removes invasive alien plant\nspecies (ITR) from the local environment and turns them into sustainable\nboutique products, alongside workshops that teach the removal and\nprocessing methods. The site needed to carry that story — the problem, the\nprocess, the shop, the workshops — on a single scrolling page.",[119,1624,451],{"id":450},[11,1626,1627],{},"A responsive front end in plain HTML, CSS and JavaScript with Bootstrap,\nbuilt from a supplied design: a hero carousel, a section explaining the\ninvasive species problem in numbers, a shop preview and a workshop\nsign-up path, all on one page with anchor navigation. As with the other\nfront-end-only builds here, I wasn't the designer — the file arrived as a\nfinished design, and the job was turning it into working, responsive markup.",[119,1629,458],{"id":457},[11,1631,1632],{},"Real, delivered client work: a full front end built to a supplied design,\nhanded off rather than deployed by me — which is why it carries no live link.",{"title":91,"searchDepth":92,"depth":92,"links":1634},[1635,1636,1637],{"id":443,"depth":212,"text":444},{"id":450,"depth":212,"text":451},{"id":457,"depth":212,"text":458},{"src":1639,"alt":1640,"width":97,"height":676},"\u002Fimages\u002Fprojects\u002Fapplause-cover.webp","The Applause homepage hero, showing a hand-held picture frame over water beneath the headline about the future of native plant species",[1642,1643,1645,1647],{"label":475,"value":476},{"label":478,"value":1644},"Applause — EU-funded ITR project, Slovenia",{"label":481,"value":1646},"Single-page site, frontend only",{"label":484,"value":485},{},[],8,"\u002Fprojects\u002Fapplause","Environmental & sustainability",{"title":1615,"description":91},[],[517,518,519,520],"projects\u002Fapplause","A single-page front end for a Slovenian EU-funded project that removes invasive plant species and turns them into sustainable products — built from a supplied design.","HoLGyFFieCplBjxUHRDiKebLb3AISJ1vuPUlfnWHLsc",{"id":1660,"title":1661,"body":1662,"cover":1684,"description":91,"eta":472,"extension":101,"facts":1687,"featured":100,"featuredOrder":472,"kicker":486,"liveUrl":472,"meta":1693,"metrics":1694,"navigation":103,"order":1695,"path":1696,"placeholder":100,"repoUrl":472,"role":491,"sector":1697,"seo":1698,"shots":1699,"stack":1730,"status":521,"stem":1731,"summary":1732,"type":524,"year":525,"__hash__":1733},"projects\u002Fprojects\u002Fattribute-x.md","AttributeX Solutions",{"type":8,"value":1663,"toc":1679},[1664,1666,1669,1671,1674,1676],[119,1665,444],{"id":443},[11,1667,1668],{},"AttributeX Solutions is a B2B sales and customer service consultancy. The\nbrief was a seven-page site — services, a filterable news feed with blog\ncards, testimonials, a \"who we are\" page and a contact\u002Fquote flow — matched\nto a given design system pixel-for-pixel.",[119,1670,451],{"id":450},[11,1672,1673],{},"A responsive front end in HTML, CSS and JavaScript with Bootstrap: isometric\nillustrations, a filterable news feed backed by dynamic-looking blog cards,\na testimonials carousel and a newsletter signup in the footer, across seven\npages that share one consistent design system. As with the other front-end\nbuilds here, the design arrived finished — the job was turning it into\nworking, responsive markup across every page, not just the homepage.",[119,1675,458],{"id":457},[11,1677,1678],{},"Real, delivered client work: a multi-page site built to a supplied design,\nhanded off rather than deployed by me — which is why it carries no live link.",{"title":91,"searchDepth":92,"depth":92,"links":1680},[1681,1682,1683],{"id":443,"depth":212,"text":444},{"id":450,"depth":212,"text":451},{"id":457,"depth":212,"text":458},{"src":1685,"alt":1686,"width":97,"height":676},"\u002Fimages\u002Fprojects\u002Fattribute-x-cover.webp","The AttributeX Solutions site navigation and a news section headlined \"Tech-Savvy!\" beside an isometric illustration of a person working at a laptop",[1688,1689,1690,1692],{"label":475,"value":476},{"label":478,"value":1661},{"label":481,"value":1691},"7-page responsive site, frontend only",{"label":484,"value":485},{},[],9,"\u002Fprojects\u002Fattribute-x","B2B consulting",{"title":1661,"description":91},[1700,1704,1709,1714,1718,1723,1726],{"src":1701,"alt":1702,"width":498,"height":1703,"caption":500},"\u002Fimages\u002Fprojects\u002Fattribute-x-home.webp","The AttributeX Solutions homepage hero and services overview",2003,{"src":1705,"alt":1706,"width":498,"height":1707,"caption":1708},"\u002Fimages\u002Fprojects\u002Fattribute-x-service.webp","The AttributeX Solutions services page listing sales and customer service offerings",3016,"Services",{"src":1710,"alt":1711,"width":498,"height":1712,"caption":1713},"\u002Fimages\u002Fprojects\u002Fattribute-x-who-we-are.webp","The AttributeX Solutions \"Who we are\" page",2347,"Who we are",{"src":1715,"alt":1716,"width":498,"height":1703,"caption":1717},"\u002Fimages\u002Fprojects\u002Fattribute-x-news.webp","The AttributeX Solutions filterable news and blog page","News",{"src":1719,"alt":1720,"width":498,"height":1721,"caption":1722},"\u002Fimages\u002Fprojects\u002Fattribute-x-testimonials.webp","The AttributeX Solutions testimonials page",2034,"Testimonials",{"src":1724,"alt":1725,"width":498,"height":498,"caption":1011},"\u002Fimages\u002Fprojects\u002Fattribute-x-contact.webp","The AttributeX Solutions contact and quote request page",{"src":1727,"alt":1728,"width":498,"height":1729,"caption":639},"\u002Fimages\u002Fprojects\u002Fattribute-x-about.webp","The AttributeX Solutions about page",2239,[517,518,519,520],"projects\u002Fattribute-x","A seven-page responsive site for a B2B sales and customer service consultancy — services, a filterable news feed, testimonials and a quote flow, built from a supplied design.","SFswvKIpZirMDn687fAk0qLn_6CtIfGEcGSbBz5ev-s",{"id":1735,"title":1736,"body":1737,"cover":1758,"description":91,"eta":472,"extension":101,"facts":1761,"featured":100,"featuredOrder":472,"kicker":486,"liveUrl":472,"meta":1768,"metrics":1769,"navigation":103,"order":1770,"path":1771,"placeholder":100,"repoUrl":472,"role":491,"sector":1772,"seo":1773,"shots":1774,"stack":1791,"status":521,"stem":1792,"summary":1793,"type":524,"year":525,"__hash__":1794},"projects\u002Fprojects\u002Felder-care.md","Elder Care",{"type":8,"value":1738,"toc":1753},[1739,1741,1744,1746,1749,1751],[119,1740,444],{"id":443},[11,1742,1743],{},"Elder Care is a Singapore-headquartered service connecting families with\ntrusted care and support for aging parents. The brief covered eight pages —\nservices, blog, plans and a contact\u002Flead form — matched to a supplied design.",[119,1745,451],{"id":450},[11,1747,1748],{},"A responsive front end in HTML, CSS and JavaScript with Bootstrap: a service\noverview, a team section, a blog, a pricing\u002Fplans page and a lead-capture\ncontact form, all matched to the given design across every page. As with the\nother front-end builds here, the design arrived finished — the job was the\nresponsive markup, not the visual design itself.",[119,1750,458],{"id":457},[11,1752,1678],{},{"title":91,"searchDepth":92,"depth":92,"links":1754},[1755,1756,1757],{"id":443,"depth":212,"text":444},{"id":450,"depth":212,"text":451},{"id":457,"depth":212,"text":458},{"src":1759,"alt":1760,"width":470,"height":471},"\u002Fimages\u002Fprojects\u002Fec-website-cover.webp","The Elder Care homepage hero, \"Parents living away?\", beside an older couple having coffee, above a row of elder-care service cards",[1762,1763,1765,1767],{"label":475,"value":476},{"label":478,"value":1764},"Elder Care (Singapore)",{"label":481,"value":1766},"8-page responsive site, frontend only",{"label":484,"value":485},{},[],10,"\u002Fprojects\u002Felder-care","Healthcare & senior care",{"title":1736,"description":91},[1775,1779,1783,1787],{"src":1776,"alt":1777,"width":498,"height":1778,"caption":500},"\u002Fimages\u002Fprojects\u002Fec-website-home.webp","The Elder Care homepage, showing the hero, services grid and a contact form",2992,{"src":1780,"alt":1781,"width":498,"height":1782,"caption":639},"\u002Fimages\u002Fprojects\u002Fec-website-about.webp","The Elder Care about page, showing the team and how the service works",2134,{"src":1784,"alt":1785,"width":498,"height":1786,"caption":1708},"\u002Fimages\u002Fprojects\u002Fec-website-service.webp","The Elder Care services page",1392,{"src":1788,"alt":1789,"width":498,"height":1790,"caption":1011},"\u002Fimages\u002Fprojects\u002Fec-website-contact.webp","The Elder Care contact page",1202,[517,518,519,520],"projects\u002Felder-care","An eight-page responsive website for a Singapore-based elder care service, connecting families with trusted care and support — built from a supplied design.","mtIxPilYinekJ-GTeQTg8pk8NlAElcxJltCaoXw2XuQ",{"id":1796,"title":1797,"body":1798,"cover":1820,"description":91,"eta":472,"extension":101,"facts":1823,"featured":100,"featuredOrder":472,"kicker":486,"liveUrl":472,"meta":1829,"metrics":1830,"navigation":103,"order":1831,"path":1832,"placeholder":100,"repoUrl":472,"role":491,"sector":1833,"seo":1834,"shots":1835,"stack":1855,"status":521,"stem":1856,"summary":1857,"type":524,"year":525,"__hash__":1858},"projects\u002Fprojects\u002Felite-competitions.md","Elite Competitions",{"type":8,"value":1799,"toc":1815},[1800,1802,1805,1807,1810,1812],[119,1801,444],{"id":443},[11,1803,1804],{},"Elite Competitions is an online prize-competition platform — \"Dream, Enter,\nWin.\" The brief was a full competition-entry flow: browsing live\ncompetitions, selecting tickets, checking out, and following a draw through\nto its winner, spread across roughly twenty pages.",[119,1806,451],{"id":450},[11,1808,1809],{},"A responsive front end in HTML, CSS and JavaScript with Bootstrap: competition\nlistings, a ticket-selection and cart flow, countdown timers on live draws, an\nimage lightbox for prize galleries, full account flows (login, register,\nrecovery, my account) and a winners' podium — all matched to a supplied\ndesign across every page.",[119,1811,458],{"id":457},[11,1813,1814],{},"Real, delivered client work: a large multi-page site built to a supplied\ndesign, handed off rather than deployed by me — which is why it carries no\nlive link.",{"title":91,"searchDepth":92,"depth":92,"links":1816},[1817,1818,1819],{"id":443,"depth":212,"text":444},{"id":450,"depth":212,"text":451},{"id":457,"depth":212,"text":458},{"src":1821,"alt":1822,"width":470,"height":471},"\u002Fimages\u002Fprojects\u002Felite-cover.webp","The Elite Competitions homepage hero, \"We Give Amazing Prizes\", showing a car giveaway slide above a \"How It Works\" section",[1824,1825,1826,1828],{"label":475,"value":476},{"label":478,"value":1797},{"label":481,"value":1827},"20-page responsive site, frontend only",{"label":484,"value":485},{},[],11,"\u002Fprojects\u002Felite-competitions","Online competitions & prize draws",{"title":1797,"description":91},[1836,1840,1845,1850],{"src":1837,"alt":1838,"width":498,"height":1839,"caption":500},"\u002Fimages\u002Fprojects\u002Felite-home.webp","The Elite Competitions homepage with prize carousel and how-it-works section",3603,{"src":1841,"alt":1842,"width":498,"height":1843,"caption":1844},"\u002Fimages\u002Fprojects\u002Felite-active-competitions.webp","The Elite Competitions active competitions listing page",1391,"Active competitions",{"src":1846,"alt":1847,"width":498,"height":1848,"caption":1849},"\u002Fimages\u002Fprojects\u002Felite-competition-details.webp","A competition details page with ticket selection",1061,"Competition details",{"src":1851,"alt":1852,"width":498,"height":1853,"caption":1854},"\u002Fimages\u002Fprojects\u002Felite-winner-podium.webp","The winners' podium page showing past prize winners",1543,"Winners' podium",[517,518,519,520],"projects\u002Felite-competitions","An online prize-competition platform — competition listings, ticket selection, live draws and a winners' podium — built from a supplied design.","dtDBJjaqruAev-ZZd13Bgte2YezzY6Lgo3ofsbS-2YU",{"id":1860,"title":1861,"body":1862,"cover":1884,"description":91,"eta":472,"extension":101,"facts":1887,"featured":100,"featuredOrder":472,"kicker":486,"liveUrl":472,"meta":1895,"metrics":1896,"navigation":103,"order":1897,"path":1898,"placeholder":100,"repoUrl":472,"role":491,"sector":1899,"seo":1900,"shots":1901,"stack":1920,"status":521,"stem":1921,"summary":1922,"type":524,"year":525,"__hash__":1923},"projects\u002Fprojects\u002Feu-skladi.md","EU-Skladi",{"type":8,"value":1863,"toc":1879},[1864,1866,1869,1871,1874,1876],[119,1865,444],{"id":443},[11,1867,1868],{},"A data-driven institutional portal for an EU-funded program in Slovenia,\ncovering programs, projects, open calls (razpisi), news and events, aimed at\napplicants and the public tracking how EU funding is used in the region.",[119,1870,451],{"id":450},[11,1872,1873],{},"A responsive front end in HTML, CSS and JavaScript with Bootstrap: a\nmega-menu navigation across twelve pages, Chart.js graphs for funding\nstatistics, a Google Maps view of regional projects, and a chat-widget\ninterface, all matched to a supplied design. As with the other builds here,\nthe design and content were given — the job was the working, responsive\nmarkup.",[119,1875,458],{"id":457},[11,1877,1878],{},"Real, delivered client work: a data-heavy institutional site built to a\nsupplied design, handed off rather than deployed by me — which is why it\ncarries no live link.",{"title":91,"searchDepth":92,"depth":92,"links":1880},[1881,1882,1883],{"id":443,"depth":212,"text":444},{"id":450,"depth":212,"text":451},{"id":457,"depth":212,"text":458},{"src":1885,"alt":1886,"width":470,"height":471},"\u002Fimages\u002Fprojects\u002Feu-skladi-cover.webp","The EU-Skladi homepage hero, \"Na strani E-skladov\", with a Programs, Projects, Razpisi and Novice card row beneath it",[1888,1889,1891,1893],{"label":475,"value":476},{"label":478,"value":1890},"EU-funded program portal, Slovenia",{"label":481,"value":1892},"12-page responsive site, frontend only",{"label":484,"value":1894},"HTML5, CSS3, JS, Bootstrap, Chart.js",{},[],12,"\u002Fprojects\u002Feu-skladi","EU-funded institutional portal",{"title":1861,"description":91},[1902,1906,1911,1916],{"src":1903,"alt":1904,"width":498,"height":1905,"caption":500},"\u002Fimages\u002Fprojects\u002Feu-skladi-home.webp","The EU-Skladi homepage with program cards and a news section",4714,{"src":1907,"alt":1908,"width":498,"height":1909,"caption":1910},"\u002Fimages\u002Fprojects\u002Feu-skladi-projekti.webp","The EU-Skladi projects listing page",1809,"Projects",{"src":1912,"alt":1913,"width":498,"height":1914,"caption":1915},"\u002Fimages\u002Fprojects\u002Feu-skladi-razpisi.webp","The EU-Skladi open calls (razpisi) page",2025,"Open calls",{"src":1917,"alt":1918,"width":498,"height":1919,"caption":639},"\u002Fimages\u002Fprojects\u002Feu-skladi-o-nas.webp","The EU-Skladi about page",1457,[517,518,519,520,1607],"projects\u002Feu-skladi","An institutional portal for an EU-funded program in Slovenia — programs, projects, open calls and news, with interactive charts and a Google Maps view — built from a supplied design.","9Jhxj9SkR-MjPbuP6sRYJuJde_Uy1CGqXdM7MjzOLAY",{"id":1925,"title":1926,"body":1927,"cover":1949,"description":91,"eta":472,"extension":101,"facts":1952,"featured":100,"featuredOrder":472,"kicker":486,"liveUrl":472,"meta":1958,"metrics":1959,"navigation":103,"order":1960,"path":1961,"placeholder":100,"repoUrl":472,"role":491,"sector":1962,"seo":1963,"shots":1964,"stack":1984,"status":521,"stem":1985,"summary":1986,"type":524,"year":525,"__hash__":1987},"projects\u002Fprojects\u002Ffreelance-engine.md","FreelanceEngine",{"type":8,"value":1928,"toc":1944},[1929,1931,1934,1936,1939,1941],[119,1930,444],{"id":443},[11,1932,1933],{},"FreelanceEngine is a freelance-marketplace concept in the spirit of Upwork\nand Fiverr. The brief was an eleven-page frontend: browsing and filtering\nprojects, freelancer profiles, and the two core flows — posting a project and\ncreating a profile — each as a multi-step form.",[119,1935,451],{"id":450},[11,1937,1938],{},"A responsive front end in HTML, CSS and JavaScript with Bootstrap and jQuery\nUI: project listing tables with filters, tag-based skill inputs, a date\npicker, freelancer profile cards with ratings, and multi-step post-a-project\nand create-profile flows, all matched to a supplied design.",[119,1940,458],{"id":457},[11,1942,1943],{},"Real, delivered client work: a multi-page marketplace frontend built to a\nsupplied design, handed off rather than deployed by me — which is why it\ncarries no live link.",{"title":91,"searchDepth":92,"depth":92,"links":1945},[1946,1947,1948],{"id":443,"depth":212,"text":444},{"id":450,"depth":212,"text":451},{"id":457,"depth":212,"text":458},{"src":1950,"alt":1951,"width":470,"height":471},"\u002Fimages\u002Fprojects\u002Ffreelance-engine-cover.webp","The FreelanceEngine homepage split hero, \"The best way to find a professional\" and \"Need a job? Tell us your story\", above a project listings table",[1953,1954,1955,1957],{"label":475,"value":476},{"label":478,"value":1926},{"label":481,"value":1956},"11-page responsive site, frontend only",{"label":484,"value":485},{},[],13,"\u002Fprojects\u002Ffreelance-engine","Freelance marketplace",{"title":1926,"description":91},[1965,1969,1974,1979],{"src":1966,"alt":1967,"width":498,"height":1968,"caption":500},"\u002Fimages\u002Fprojects\u002Ffreelanceengine-home.webp","The FreelanceEngine homepage with a project listings table and freelancer profile card",2548,{"src":1970,"alt":1971,"width":498,"height":1972,"caption":1973},"\u002Fimages\u002Fprojects\u002Ffreelanceengine-profile-list.webp","The FreelanceEngine freelancer profile listing page",968,"Freelancer profiles",{"src":1975,"alt":1976,"width":498,"height":1977,"caption":1978},"\u002Fimages\u002Fprojects\u002Ffreelanceengine-project.webp","A single project detail page on FreelanceEngine",1337,"Project detail",{"src":1980,"alt":1981,"width":498,"height":1982,"caption":1983},"\u002Fimages\u002Fprojects\u002Ffreelanceengine-submit.webp","The multi-step \"submit a project\" flow on FreelanceEngine",801,"Post a project",[517,518,519,520],"projects\u002Ffreelance-engine","An 11-page freelance-marketplace frontend, inspired by Upwork and Fiverr — project listings, freelancer profiles and a multi-step post-a-project flow — built from a supplied design.","_SY1LlOqgEedgF3d3PV7ytNRR3oYEq6DiRH4Sv30wRM",{"id":1989,"title":1990,"body":1991,"cover":2012,"description":91,"eta":472,"extension":101,"facts":2015,"featured":100,"featuredOrder":472,"kicker":486,"liveUrl":472,"meta":2021,"metrics":2022,"navigation":103,"order":2023,"path":2024,"placeholder":100,"repoUrl":472,"role":491,"sector":2025,"seo":2026,"shots":2027,"stack":2047,"status":521,"stem":2048,"summary":2049,"type":524,"year":525,"__hash__":2050},"projects\u002Fprojects\u002Fholders.md","Holders",{"type":8,"value":1992,"toc":2007},[1993,1995,1998,2000,2003,2005],[119,1994,444],{"id":443},[11,1996,1997],{},"Holders is a US-to-Nigeria package-forwarding service: shop any US retailer,\nship to Nigeria. The brief covered the full customer journey across sixteen\npages — shipping cost calculator, currency converter, order consolidation, a\nbrand marketplace and a complete account system.",[119,1999,451],{"id":450},[11,2001,2002],{},"A responsive front end in HTML, CSS and JavaScript with Bootstrap: a\nshipping-cost calculator, a currency converter, cart and checkout, order\nconsolidation, a favourite-store list, and full account flows — login,\nsign-up, address book, payment — all matched to a supplied design across\nevery page.",[119,2004,458],{"id":457},[11,2006,1814],{},{"title":91,"searchDepth":92,"depth":92,"links":2008},[2009,2010,2011],{"id":443,"depth":212,"text":444},{"id":450,"depth":212,"text":451},{"id":457,"depth":212,"text":458},{"src":2013,"alt":2014,"width":470,"height":471},"\u002Fimages\u002Fprojects\u002Fholders-cover.webp","The Holders homepage hero, \"Shopping in USA now made easy\", showing a package-forwarding illustration and shipping calculator, product marketplace and account tiles",[2016,2017,2018,2020],{"label":475,"value":476},{"label":478,"value":1990},{"label":481,"value":2019},"16-page responsive site, frontend only",{"label":484,"value":485},{},[],14,"\u002Fprojects\u002Fholders","Package forwarding & logistics",{"title":1990,"description":91},[2028,2032,2037,2042],{"src":2029,"alt":2030,"width":498,"height":2031,"caption":500},"\u002Fimages\u002Fprojects\u002Fholders-home.webp","The Holders homepage with shipping calculator and top brands",3063,{"src":2033,"alt":2034,"width":498,"height":2035,"caption":2036},"\u002Fimages\u002Fprojects\u002Fholders-calculator.webp","The Holders shipping cost calculator page",2151,"Shipping calculator",{"src":2038,"alt":2039,"width":498,"height":2040,"caption":2041},"\u002Fimages\u002Fprojects\u002Fholders-product.webp","A product page on Holders",1004,"Product",{"src":2043,"alt":2044,"width":498,"height":2045,"caption":2046},"\u002Fimages\u002Fprojects\u002Fholders-account.webp","The Holders account page",1006,"Account",[517,518,519,520],"projects\u002Fholders","A 16-page package-forwarding site connecting US shopping to Nigeria — a shipping calculator, currency converter, order consolidation and a full account system — built from a supplied design.","1Dx5EGuLgINNNfTHZIKO_vYUUoFfYUn6mihKw5dVu3o",{"id":437,"title":438,"body":2052,"cover":2071,"description":91,"eta":472,"extension":101,"facts":2072,"featured":100,"featuredOrder":472,"kicker":486,"liveUrl":472,"meta":2077,"metrics":2078,"navigation":103,"order":489,"path":490,"placeholder":100,"repoUrl":472,"role":491,"sector":492,"seo":2079,"shots":2080,"stack":2085,"status":521,"stem":522,"summary":523,"type":524,"year":525,"__hash__":526},{"type":8,"value":2053,"toc":2066},[2054,2056,2058,2060,2062,2064],[119,2055,444],{"id":443},[11,2057,447],{},[119,2059,451],{"id":450},[11,2061,454],{},[119,2063,458],{"id":457},[11,2065,461],{},{"title":91,"searchDepth":92,"depth":92,"links":2067},[2068,2069,2070],{"id":443,"depth":212,"text":444},{"id":450,"depth":212,"text":451},{"id":457,"depth":212,"text":458},{"src":468,"alt":469,"width":470,"height":471},[2073,2074,2075,2076],{"label":475,"value":476},{"label":478,"value":479},{"label":481,"value":482},{"label":484,"value":485},{},[],{"title":438,"description":91},[2081,2082,2083,2084],{"src":496,"alt":497,"width":498,"height":499,"caption":500},{"src":502,"alt":503,"width":498,"height":504,"caption":505},{"src":507,"alt":508,"width":498,"height":509,"caption":510},{"src":512,"alt":513,"width":498,"height":514,"caption":515},[517,518,519,520],{"id":2087,"title":2088,"body":2089,"cover":2111,"description":91,"eta":472,"extension":101,"facts":2115,"featured":100,"featuredOrder":472,"kicker":486,"liveUrl":472,"meta":2120,"metrics":2121,"navigation":103,"order":2122,"path":2123,"placeholder":100,"repoUrl":472,"role":491,"sector":2124,"seo":2125,"shots":2126,"stack":2143,"status":521,"stem":2144,"summary":2145,"type":524,"year":525,"__hash__":2146},"projects\u002Fprojects\u002Fkids-zone.md","Kids Zone",{"type":8,"value":2090,"toc":2106},[2091,2093,2096,2098,2101,2103],[119,2092,444],{"id":443},[11,2094,2095],{},"Kids Zone is an online gaming portal for kids: browse games by category,\nopen a game page, and manage an account. The brief was an eight-page\nfrontend matched to a supplied design.",[119,2097,451],{"id":450},[11,2099,2100],{},"A responsive front end in HTML, CSS and JavaScript with Bootstrap: a\ntabbed Popular\u002FLatest game grid, category and game-detail pages, an FAQ page\nand an account-validation flow, all matched to a supplied design.",[119,2102,458],{"id":457},[11,2104,2105],{},"Real, delivered client work: a multi-page site built to a supplied design,\nhanded off rather than deployed by me — which is why it carries no live\nlink.",{"title":91,"searchDepth":92,"depth":92,"links":2107},[2108,2109,2110],{"id":443,"depth":212,"text":444},{"id":450,"depth":212,"text":451},{"id":457,"depth":212,"text":458},{"src":2112,"alt":2113,"width":97,"height":2114},"\u002Fimages\u002Fprojects\u002Fkidszone-cover.webp","The Kids Zone homepage, showing \"Popular\" and \"Latest\" game category tabs above a grid of game thumbnails",764,[2116,2117,2118,2119],{"label":475,"value":476},{"label":478,"value":2088},{"label":481,"value":1766},{"label":484,"value":485},{},[],16,"\u002Fprojects\u002Fkids-zone","Kids' gaming portal",{"title":2088,"description":91},[2127,2131,2135,2139],{"src":2128,"alt":2129,"width":498,"height":2130,"caption":500},"\u002Fimages\u002Fprojects\u002Fkidszone-home.webp","The Kids Zone homepage with Popular and Latest game tabs",573,{"src":2132,"alt":2133,"width":498,"height":2130,"caption":2134},"\u002Fimages\u002Fprojects\u002Fkidszone-category.webp","The Kids Zone category browsing page","Category",{"src":2136,"alt":2137,"width":498,"height":2130,"caption":2138},"\u002Fimages\u002Fprojects\u002Fkidszone-gamepage.webp","A single game page on Kids Zone","Game page",{"src":2140,"alt":2141,"width":498,"height":2142,"caption":639},"\u002Fimages\u002Fprojects\u002Fkidszone-about.webp","The Kids Zone about page",1503,[517,518,519,520],"projects\u002Fkids-zone","An online gaming portal for kids — game categories, a game detail page and an account flow — built from a supplied design.","JiNeo99GdZKmNu9fdGISbmjJGpEHlZmbwGxEpx-S0ew",{"id":2148,"title":2149,"body":2150,"cover":2172,"description":91,"eta":472,"extension":101,"facts":2175,"featured":100,"featuredOrder":472,"kicker":486,"liveUrl":472,"meta":2181,"metrics":2182,"navigation":103,"order":2183,"path":2184,"placeholder":100,"repoUrl":472,"role":491,"sector":2185,"seo":2186,"shots":2187,"stack":2202,"status":521,"stem":2203,"summary":2204,"type":524,"year":525,"__hash__":2205},"projects\u002Fprojects\u002Fnero-whyte.md","Nero Whyte",{"type":8,"value":2151,"toc":2167},[2152,2154,2157,2159,2162,2164],[119,2153,444],{"id":443},[11,2155,2156],{},"Nero Whyte is an international fashion brand. The brief was a six-page site\nbuilt around a monochrome, editorial visual identity — a portfolio\u002Flookbook\nsection and the brand's story — matched to a supplied design.",[119,2158,451],{"id":450},[11,2160,2161],{},"A responsive front end in HTML, CSS and JavaScript with Bootstrap: a\nminimal, high-contrast layout, an image-lightbox-driven portfolio section,\nand a blog\u002Fsingle post template, all matched to a supplied design.",[119,2163,458],{"id":457},[11,2165,2166],{},"Real, delivered client work: a multi-page brand site built to a supplied\ndesign, handed off rather than deployed by me — which is why it carries no\nlive link.",{"title":91,"searchDepth":92,"depth":92,"links":2168},[2169,2170,2171],{"id":443,"depth":212,"text":444},{"id":450,"depth":212,"text":451},{"id":457,"depth":212,"text":458},{"src":2173,"alt":2174,"width":470,"height":471},"\u002Fimages\u002Fprojects\u002Fnero-whyte-cover.webp","The Nero Whyte site on a laptop and tablet, showing the black-and-white \"We Are Transparent\" page beside the brand's zigzag logo",[2176,2177,2178,2180],{"label":475,"value":476},{"label":478,"value":2149},{"label":481,"value":2179},"6-page responsive site, frontend only",{"label":484,"value":485},{},[],17,"\u002Fprojects\u002Fnero-whyte","Fashion & apparel",{"title":2149,"description":91},[2188,2191,2195,2199],{"src":2189,"alt":2190,"width":498,"height":2130,"caption":500},"\u002Fimages\u002Fprojects\u002Fnero-whyte-home.webp","The Nero Whyte homepage",{"src":2192,"alt":2193,"width":498,"height":2130,"caption":2194},"\u002Fimages\u002Fprojects\u002Fnero-whyte-portfolio-1.webp","The Nero Whyte portfolio\u002Flookbook page","Portfolio",{"src":2196,"alt":2197,"width":498,"height":2130,"caption":2198},"\u002Fimages\u002Fprojects\u002Fnero-whyte-portfolio-2.webp","A second Nero Whyte portfolio page","Portfolio, detail",{"src":2200,"alt":2201,"width":498,"height":2130,"caption":639},"\u002Fimages\u002Fprojects\u002Fnero-whyte-about.webp","The Nero Whyte about page telling the brand's story",[517,518,519,520],"projects\u002Fnero-whyte","A six-page site for an international fashion brand — a portfolio\u002Flookbook section and brand story — built from a supplied design.","drxpXAPSExVbVS_jOU_PInvOSKzJyQwA_-5Hc2SXK5Q",{"id":2207,"title":2208,"body":2209,"cover":2231,"description":91,"eta":472,"extension":101,"facts":2234,"featured":100,"featuredOrder":472,"kicker":486,"liveUrl":472,"meta":2240,"metrics":2241,"navigation":103,"order":2242,"path":2243,"placeholder":100,"repoUrl":472,"role":491,"sector":2244,"seo":2245,"shots":2246,"stack":2266,"status":521,"stem":2267,"summary":2268,"type":524,"year":525,"__hash__":2269},"projects\u002Fprojects\u002Fpricesearch.md","Pricesearch",{"type":8,"value":2210,"toc":2226},[2211,2213,2216,2218,2221,2223],[119,2212,444],{"id":443},[11,2214,2215],{},"Pricesearch is a competitor price-monitoring tool for webshop owners and\nbrand managers. The brief was a 10-page marketing site — an animated hero\nwith a live-demo feel, features, a four-step \"how it works\", pricing, and a\nblog\u002Fresource section — matched to a supplied design.",[119,2217,451],{"id":450},[11,2219,2220],{},"A responsive front end in HTML, CSS and JavaScript with Bootstrap: scroll\nanimations, a tag-input demo for tracking competitors, a pricing table, and\na blog with archive and single-post templates, all matched to a supplied\ndesign.",[119,2222,458],{"id":457},[11,2224,2225],{},"Real, delivered client work: a marketing site built to a supplied design,\nhanded off rather than deployed by me — which is why it carries no live\nlink.",{"title":91,"searchDepth":92,"depth":92,"links":2227},[2228,2229,2230],{"id":443,"depth":212,"text":444},{"id":450,"depth":212,"text":451},{"id":457,"depth":212,"text":458},{"src":2232,"alt":2233,"width":470,"height":471},"\u002Fimages\u002Fprojects\u002Fpricesearch-cover.webp","The Pricesearch homepage hero, \"Easily monitor your competitors' prices\", beside a competitor price-tracking dashboard mockup",[2235,2236,2237,2239],{"label":475,"value":476},{"label":478,"value":2208},{"label":481,"value":2238},"10-page responsive site, frontend only",{"label":484,"value":485},{},[],18,"\u002Fprojects\u002Fpricesearch","SaaS marketing",{"title":2208,"description":91},[2247,2251,2256,2261],{"src":2248,"alt":2249,"width":498,"height":2250,"caption":500},"\u002Fimages\u002Fprojects\u002Fpricesearch-home.webp","The Pricesearch homepage with an animated hero and price-tracking dashboard mockup",2571,{"src":2252,"alt":2253,"width":498,"height":2254,"caption":2255},"\u002Fimages\u002Fprojects\u002Fpricesearch-features.webp","The Pricesearch features page",2664,"Features",{"src":2257,"alt":2258,"width":498,"height":2259,"caption":2260},"\u002Fimages\u002Fprojects\u002Fpricesearch-pricing.webp","The Pricesearch pricing page",1772,"Pricing",{"src":2262,"alt":2263,"width":498,"height":2264,"caption":2265},"\u002Fimages\u002Fprojects\u002Fpricesearch-blog.webp","The Pricesearch blog page",2197,"Blog",[517,518,519,520],"projects\u002Fpricesearch","A 10-page SaaS marketing site for a competitor price-monitoring tool — animated hero, features, pricing and a blog\u002Fresource section — built from a supplied design.","YrnAAgeXcZKNuZMJPHJW469HM5qhfqGg_H7jJmDBzSs",{"id":2271,"title":2272,"body":2273,"cover":2295,"description":91,"eta":472,"extension":101,"facts":2298,"featured":100,"featuredOrder":472,"kicker":486,"liveUrl":472,"meta":2304,"metrics":2305,"navigation":103,"order":2306,"path":2307,"placeholder":100,"repoUrl":472,"role":491,"sector":2308,"seo":2309,"shots":2310,"stack":2330,"status":521,"stem":2331,"summary":2332,"type":524,"year":525,"__hash__":2333},"projects\u002Fprojects\u002Fspire-global.md","Spire Global",{"type":8,"value":2274,"toc":2290},[2275,2277,2280,2282,2285,2287],[119,2276,444],{"id":443},[11,2278,2279],{},"Spire Global's corporate sustainability site tells its story across five\nconsistent templates: mission and stakeholder highlights, sustainability\nreports, and a set of focus-area pages — environment, people, user\nexperience — each built around GRI and SDG indicator grids.",[119,2281,451],{"id":450},[11,2283,2284],{},"A responsive front end in HTML, CSS and JavaScript with Bootstrap: rotating\nstakeholder highlight tiles, scroll counters, a lightbox news gallery, and\ninteractive GRI\u002FSDG indicator grids, all reused consistently across the five\npage templates and matched to a supplied design.",[119,2286,458],{"id":457},[11,2288,2289],{},"Real, delivered client work: a multi-template corporate site built to a\nsupplied design, handed off rather than deployed by me — which is why it\ncarries no live link.",{"title":91,"searchDepth":92,"depth":92,"links":2291},[2292,2293,2294],{"id":443,"depth":212,"text":444},{"id":450,"depth":212,"text":451},{"id":457,"depth":212,"text":458},{"src":2296,"alt":2297,"width":470,"height":471},"\u002Fimages\u002Fprojects\u002Fspire-global-cover.webp","The Spire Global homepage hero, \"Sustainability. A greener tomorrow.\", above a mission statement and forest photograph",[2299,2300,2301,2303],{"label":475,"value":476},{"label":478,"value":2272},{"label":481,"value":2302},"5-template responsive site, frontend only",{"label":484,"value":485},{},[],19,"\u002Fprojects\u002Fspire-global","Corporate sustainability",{"title":2272,"description":91},[2311,2315,2320,2325],{"src":2312,"alt":2313,"width":676,"height":2314,"caption":500},"\u002Fimages\u002Fprojects\u002Fspire-global-home.webp","The Spire Global homepage with sustainability highlights and report downloads",5668,{"src":2316,"alt":2317,"width":676,"height":2318,"caption":2319},"\u002Fimages\u002Fprojects\u002Fspire-global-environment.webp","The Spire Global environment focus-area page",4728,"Environment",{"src":2321,"alt":2322,"width":676,"height":2323,"caption":2324},"\u002Fimages\u002Fprojects\u002Fspire-global-people.webp","The Spire Global people focus-area page",4664,"People",{"src":2326,"alt":2327,"width":676,"height":2328,"caption":2329},"\u002Fimages\u002Fprojects\u002Fspire-global-user-experience.webp","The Spire Global user-experience focus-area page",4320,"User experience",[517,518,519,520],"projects\u002Fspire-global","A five-template corporate sustainability site — mission, sustainability focus areas, reports and interactive GRI\u002FSDG indicator grids — built from a supplied design.","iBcvv3h64wlWekkwvzLCL3M9SZLdfXH_5d1LfYBdGBs",1789845711092]