DEV Community

SIKOUTRIS
SIKOUTRIS

Posted on

Building a No-Code Website Builder for Tradespeople Who Hate Technology

Most website builders assume their users are at least somewhat tech-savvy. Squarespace expects you to understand layout concepts. WordPress expects you to manage plugins. Even Wix assumes you know what a "section" is.

French artisans — plumbers, electricians, carpenters, roofers — generally do not care about any of that. They want a website that shows up on Google when someone searches for their trade in their town. That is it. We built Mon Site Artisan specifically for this audience, and the engineering constraints were surprisingly interesting.

The Design Constraint: Zero Learning Curve

Our target user profile:

  • Age 35-55
  • Uses a smartphone daily but a computer rarely
  • Has never edited HTML, CSS, or any code
  • Wants a website in under 15 minutes
  • Needs it to rank locally on Google

This means no drag-and-drop editors, no template marketplaces, no widget libraries. Instead, we built a wizard-based flow that asks questions and generates a complete website from the answers.

The Wizard Architecture

class SiteWizard {
    private array $steps = [
        1 => "trade_selection",
        2 => "business_info",
        3 => "service_area",
        4 => "photo_upload",
        5 => "review_and_publish"
    ];

    public function processStep(int $step, array $data): array {
        return match($step) {
            1 => $this->handleTradeSelection($data),
            2 => $this->handleBusinessInfo($data),
            3 => $this->handleServiceArea($data),
            4 => $this->handlePhotoUpload($data),
            5 => $this->handlePublish($data),
        };
    }

    private function handleTradeSelection(array $data): array {
        $trade = $data["trade"];
        // Pre-load trade-specific content templates
        $template = TradeTemplates::get($trade);
        return [
            "next_step" => 2,
            "prefilled" => [
                "services" => $template->defaultServices(),
                "seo_keywords" => $template->suggestedKeywords(),
                "color_scheme" => $template->recommendedColors()
            ]
        ];
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 1 is the key differentiator. When a plumber selects "plombier" (plumber), the system pre-fills everything with plumbing-specific defaults:

  • Service descriptions ("Depannage plomberie", "Installation sanitaire", "Detection fuite")
  • Color scheme (blue tones — industry convention)
  • SEO keywords ("plombier + [their city]")
  • Schema markup (Plumber type)
  • Stock photos (tools, pipes, bathroom installations)

The artisan only needs to add their name, phone number, city, and optionally upload their own photos.

Trade-Specific Template Engine

We maintain templates for 25 trades. Each template includes:

class PlumberTemplate extends TradeTemplate {
    public string $trade = "plombier";
    public string $schema_type = "Plumber";

    public function defaultServices(): array {
        return [
            ["name" => "Depannage plomberie", "description" => "Intervention rapide pour fuites, canalisations bouchees, et pannes."],
            ["name" => "Installation sanitaire", "description" => "Pose de douche, baignoire, WC, lavabo et robinetterie."],
            ["name" => "Chauffe-eau", "description" => "Installation, remplacement et entretien de chauffe-eau."],
            ["name" => "Detection de fuites", "description" => "Recherche et reparation de fuites non visibles."],
        ];
    }

    public function suggestedKeywords(): array {
        return ["plombier", "depannage plomberie", "plombier urgence", "fuite eau"];
    }

    public function recommendedColors(): array {
        return ["primary" => "#1a73e8", "secondary" => "#0d47a1", "accent" => "#4fc3f7"];
    }

    public function schemaMarkup(array $businessData): array {
        return [
            "@context" => "https://schema.org",
            "@type" => "Plumber",
            "name" => $businessData["company_name"],
            "telephone" => $businessData["phone"],
            "areaServed" => $businessData["service_area"],
            "address" => [
                "@type" => "PostalAddress",
                "addressLocality" => $businessData["city"],
                "addressCountry" => "FR"
            ]
        ];
    }
}
Enter fullscreen mode Exit fullscreen mode

Static Site Generation: Why Not WordPress

For this use case, WordPress is overkill. An artisan website has 3-5 pages that change once a year. We generate static HTML files:

class StaticSiteGenerator {
    public function generate(array $siteData): string {
        $outputDir = "/sites/{$siteData["slug"]}/";

        $pages = [
            "index.html" => $this->renderHome($siteData),
            "services/index.html" => $this->renderServices($siteData),
            "contact/index.html" => $this->renderContact($siteData),
            "mentions-legales/index.html" => $this->renderLegal($siteData),
            "sitemap.xml" => $this->renderSitemap($siteData),
            "robots.txt" => $this->renderRobots($siteData),
        ];

        foreach ($pages as $path => $content) {
            file_put_contents($outputDir . $path, $content);
        }

        // Generate optimized images
        $this->processImages($siteData["photos"], $outputDir . "images/");

        return $outputDir;
    }
}
Enter fullscreen mode Exit fullscreen mode

Benefits of static generation:

  • Speed: Sub-50ms TTFB. No database queries, no PHP execution on page load.
  • Security: No WordPress vulnerabilities, no plugin updates, no admin panel to hack.
  • Hosting cost: Pennies per month. Static files on shared hosting behind Cloudflare.
  • Reliability: Nothing to crash. HTML files just work.

Local SEO: The Core Value Proposition

The entire point of the product is Google visibility. Every generated site includes:

  1. Geo-targeted title tags: "Plombier a Lyon 7eme - [Company Name]"
  2. LocalBusiness schema: With exact coordinates from the French geocoding API
  3. City-specific content: Auto-generated paragraphs mentioning neighborhoods and surrounding towns
  4. Google Business Profile integration: Instructions to claim and link their GBP listing
  5. NAP consistency: Name, Address, Phone formatted identically across all pages
function generateLocalContent($trade, $city, $department) {
    $nearbyTowns = fetchNearbyTowns($city, radius: 20);
    $content = "Votre {$trade} a {$city} ({$department}) intervient ";
    $content .= "egalement dans les communes voisines : ";
    $content .= implode(", ", array_slice($nearbyTowns, 0, 8));
    $content .= ". Contactez-nous pour un devis gratuit.";
    return $content;
}
Enter fullscreen mode Exit fullscreen mode

Image Optimization Pipeline

Artisans upload photos from their phone — often 4MB+ JPEG files straight from the camera. Our pipeline:

function optimizeImage($inputPath, $outputDir) {
    $image = imagecreatefromjpeg($inputPath);
    $width = imagesx($image);
    $height = imagesy($image);

    $sizes = [
        "large" => 1200,
        "medium" => 800,
        "thumb" => 400
    ];

    foreach ($sizes as $name => $targetWidth) {
        if ($width > $targetWidth) {
            $ratio = $targetWidth / $width;
            $newHeight = (int)($height * $ratio);
            $resized = imagecreatetruecolor($targetWidth, $newHeight);
            imagecopyresampled($resized, $image, 0, 0, 0, 0, 
                $targetWidth, $newHeight, $width, $height);

            // Save as WebP for modern browsers
            imagewebp($resized, "{$outputDir}/{$name}.webp", 80);
            // JPEG fallback
            imagejpeg($resized, "{$outputDir}/{$name}.jpg", 82);
            imagedestroy($resized);
        }
    }
    imagedestroy($image);
}
Enter fullscreen mode Exit fullscreen mode

A 4MB phone photo becomes a 60KB WebP thumbnail. Page weight for a typical artisan site: under 500KB total.

Results

After 6 months:

  • Average time to create a site: 8 minutes
  • Average PageSpeed score: 97/100
  • Most sites appear in local search results within 4-6 weeks
  • Zero support tickets about "how to edit" — because the wizard handles everything

The lesson: sometimes the best technical decision is removing options, not adding them.


Create a professional artisan website in under 15 minutes at mon-site-artisan.net

Top comments (0)