Skip to content

Website Development

Atelier Lumière: One Studio, Two Owners, Two Ways to Book

Two specialists with separate bookings, phones and companies under one name: a website with two equal booking flows and our own SMS reminders for Serbian numbers.

Atelier Lumière: One Studio, Two Owners, Two Ways to Book
Industry
Beauty studio: lashes and facials, hairdressing
Client website
studiolepote.rs

Scope of work

Design, custom WordPress theme and content plugin, two booking flows, SMS reminder system, local SEO, accessibility and login security

Outcome

Two equal booking flows and our own SMS reminders reaching Serbian numbers since 10 October 2026; 80/80 booking checks and 0 automated WCAG violations

Client: Atelier Lumière Studio Lepote
Business: beauty treatments, silk lashes and facials; men’s and women’s hairdressing
Location: Stara Pazova, Serbia
Project type: new website, custom WordPress theme, content plugin and SMS reminder system
Timeline: 6 September - 11 October 2026 (about five weeks)
Website: studiolepote.rs


About the client

Atelier Lumière is a beauty studio in Stara Pazova opened together by two specialists from different fields. Mila is a beautician and lash artist: facials, waxing, spray tanning and silk lashes, and she also teaches silk-lash courses. Neška is a men’s and women’s hairdresser: formal styling, colouring, keratin treatments, highlights and balayage.

They share a name, a space and the idea of bringing different kinds of beauty work under one roof. Everything else is separate: appointments, phone numbers, opening hours and even the companies themselves. Our job was not to hide that behind a generic salon website, but to build one where clients never have to think about how the studio works internally.

The full studiolepote.rs home page: headline, the two service fields, the studio introduction, booking in three steps, FAQ and footer
The studiolepote.rs home page: both fields right below the headline, then the studio, booking in three steps and the FAQ.

Starting point: two businesses under one roof

At first glance this is one salon. In practice it is two separate businesses under the same name:

Lashes and faceHairdressing
BookingOnline calendar with service and time-slot selectionPhone or WhatsApp only; the hairdresser confirms the appointment and adds it to her own calendar
Opening hoursHer ownHer own, and different
PhoneSeparate numberSeparate number
Legal entitySeparate tax and registration numbersSeparate tax and registration numbers
PricesKept in the booking systemDepend on hair length and thickness, materials and time, so they are agreed directly

Only the brand, the address, the email and the space are shared. None of this could be merged for the sake of simplicity. A single set of opening hours would be wrong, a single “Book” button would send the wrong client to the wrong calendar, and a single phone number would turn one owner into the other’s receptionist.

Challenges

  1. Two booking flows, one brand. Clients do not know, and should not need to know, that these are two companies. They should reach the right person, in the right way, in a single step.
  2. Two equal owners. Neither field could look like an add-on to the other, in the navigation or on the booking button.
  3. Promised SMS reminders that never arrived. The studio’s online booking system sends email reminders but does not deliver text messages to Serbian numbers. The owners did not want to give up a tool they were used to.
  4. Hairdressing appointments bypass online booking, yet they needed reminders too.
  5. A lot of content that did not exist yet. At the start there were no photos of their work, no confirmed brands, no Google Business profile and no current offers. The site had to look finished and stay honest.
  6. The owners maintain the site themselves. Banners, packages, the gallery, biographies and opening hours had to be editable without a developer and without the risk of breaking anything.
  7. Local visibility. “Hairdresser Stara Pazova” and “lashes Stara Pazova” are different searches and should lead to different pages.

Principles we followed

  • Separate wherever the two differ: booking, contact details, opening hours, legal data, prices and treatment preparation. Combined only where things are genuinely shared: the brand, the story and the address.
  • Nothing invented. No made-up prices, reviews, ratings, brands or offers. An empty section is simply not shown.
  • Content belongs to the client, appearance to the theme. All business data lives in a separate plugin, so it survives any future redesign.
  • Works without JavaScript. Every booking button is a real link; the booking window is an enhancement, not a requirement.
  • We check what is actually live, on phones and desktops, before calling anything finished.

Booking: one button, two paths

The first version of the global “Quick booking” button went straight into the online calendar for lashes and face. Hairdressing was a small note linking to another page. For a studio with two equal owners that was not acceptable, so we changed the flow.

Now every neutral “Quick booking” button (in the header, on the home page, in the fixed bar at the bottom of the phone screen, in the mobile menu and on the Booking page) opens the same window asking “Who would you like to book with?” with two equal cards:

  • Lashes and face: online booking. The “Choose a time” button loads the calendar inside the same window, without leaving the site.
  • Hairdressing: phone or WhatsApp. The hairdresser confirms the appointment and adds it to her own calendar. The WhatsApp message is pre-filled in Serbian, so the client only has to press “send”.
The Quick booking window on the Atelier Lumière website with two equal cards: online booking for lashes and face, a call or WhatsApp for hairdressing
Every neutral “Quick booking” button opens a choice between two equal cards.

Links inside one field skip the choice. From the hairdressing page, the price list or a service card, the window opens directly at the right step, with a “Back to choice” button.

The hairdressing step of the booking window on a phone: call and WhatsApp buttons and a way back to the choice
The hairdressing step on a phone: a call or a pre-filled WhatsApp message, with “Back to choice”.

Details that are not visible at first glance but make the difference:

  • The calendar loads only when the client asks for it. The external booking system does not slow down page loading.
  • Without JavaScript every button still goes to a real address. Ctrl or Cmd + click opens it in a new tab like any normal link, and the window has a visible link to open booking in a new window if the embedded calendar cannot load.
  • The window is accessible. Focus stays inside it, Escape and a click on the backdrop close it, the page does not scroll underneath, and focus returns to the button that opened it.
  • Neither option visually “wins”. The online booking, call and WhatsApp buttons share the same font size, height and padding at every screen width.
  • Reminders are stated where the decision is made, on each card and in the FAQ.
  • Online hairdressing booking is blocked in code, not just hidden in the settings. Even if an old link remained in the database, the site would not offer it.
  • One price list, not two. Lash and face prices are kept in the booking system, where clients see them when choosing a time. The site deliberately has no second table that could drift from the real one. For hairdressing, the site explains what the price depends on.

The automated test of the booking window passed 80 out of 80 checks at 1440, 390 and 320 pixels wide: correct call and WhatsApp links, a calendar that does not load before the choice, focus handling, Escape, working without JavaScript and no horizontal overflow. Requests to the booking system were blocked during the tests, so no real appointment was booked and no message was sent.

When an online calendar pays off and when a call or a message is enough is covered in more detail in our guide to online booking on your website.

SMS reminders the booking system does not send in Serbia

The site promised clients a text reminder one hour before their appointment. The studio’s booking system sends email, but does not deliver SMS to Serbian numbers. It has no webhooks either, so it cannot tell our server when an appointment is booked or changed. Rather than asking the owners to switch away from a tool they like for its mobile app, we built our own module.

How it works

  1. Every 5 minutes a real server cron job, not WordPress’s visit-dependent “pseudo-cron”, reads upcoming appointments through the official API.
  2. For each appointment it checks whether it starts in 20 to 65 minutes and whether a reminder for that appointment and that time has already been sent.
  3. It sends one SMS through a Serbian SMS provider.

The message is short and always follows the same pattern (in Serbian; roughly “Appointment reminder, today at 14:30 with Milana”):

Atelier Lumière: Podsetnik za termin, danas u 14:30 kod Milane. Tel: 0641311718

Example SMS reminder

Details that show it was thought through

  • The right specialist, the right number. The system recognises who the appointment is with and puts that specialist’s name and phone number in the message. Hairdressing appointments get reminders even though the hairdresser enters them by hand.
  • Exactly one SMS segment. Without the Serbian letters č, ć, š, ž and đ an SMS can hold 160 characters; with any one of them, only 70. The template fits in a single segment, and before every send the code counts the parts and refuses a message that would go out in two, because two parts cost twice as much. The “è” in Lumière belongs to the basic GSM-7 set, so the brand keeps its accent without shortening the limit.
  • Serbian mobile numbers only. Numbers typed as “064…”, “+381 64…” or “00381…” are normalised to one format; everything else, including foreign numbers, is skipped.
  • No duplicates. Before sending, the system atomically records a claim with a unique key (appointment + start time), so even two simultaneous runs cannot send the same reminder twice.
  • A rescheduled appointment gets a new reminder for the new time. A cancelled one gets nothing.
  • Failures are never silent. A failed send is not retried automatically, to avoid duplicates; instead we receive an email alert. The same applies if refreshing API access fails, because reminders would otherwise quietly stop.
  • Minimal data. Only a masked phone number is logged, and records are deleted after 90 days.
  • Visitors never load it. The module runs only on the server and adds nothing to the site’s pages.

The “Z” that was not UTC

On the first day, a test appointment at 14:00 did not get its reminder at 13:00. The API returns appointment times marked “Z”, which by the standard means UTC, but they are actually local Belgrade time. The system therefore saw the appointment two hours late, and the reminder would have gone out around 14:55 saying “today at 16:00”. We found and fixed the bug the same day, before a single wrong message was sent, including the switch between summer and winter time. Right after that, a test appointment at 15:45 received its reminder at 14:45.

The lesson: documentation and real data do not always agree, so we verify an API’s behaviour with a real appointment before going live.

The system has been sending real messages since 10 October 2026. A client with an online lash or facial appointment receives an email the day before and an SMS one hour before; a client with a hairdressing appointment receives an SMS one hour before the confirmed time.

Design: warm, editorial and readable

The direction is an editorial beauty studio with a sense of hotel hospitality: warm paper, cream and coffee tones, gold accents and soft outlines. Headings are set in Newsreader and body text in Hanken Grotesk, with a handwritten brand signature used sparingly. All fonts are hosted on the studio’s own server, so the site never calls Google Fonts or any other external font service.

The design evolved through iterations based on feedback:

  • The first heading font had hairline strokes that were unreadable on phones, so we replaced it.
  • iOS displayed typographic arrows and stars as colourful emoji. All icons are now SVG.
  • An animated services ticker on the home page was removed because it added no information.
  • An oversized abstract motif in an illustration was removed because it felt unsettling.
  • The hero went from three panels to two when the studio narrowed its offer to two fields.

Until photos of their work arrive, services are accompanied by original abstract illustrations, always shown whole and never cropped. The home and About pages show real photos of the owners. Motion is minimal and purposeful: no animations hide content until you scroll, and everything switches off when the visitor has reduced motion enabled.

Content written by the owners

All business content comes from the owners: their shared story, two biographies, opening hours, business details and preparation instructions. We did not add treatments, guarantees or advice of our own.

  • Each field’s page carries the full biography of its specialist and a numbered “Preparation” section: arriving without eye make-up, the ideal time for a silk-lash refill, how to prepare hair for formal styling, when an in-person consultation is needed.
  • The Contact page has two separate blocks with opening hours and business details, a shared address and directions to the entrance.
  • Empty is better than fake. Sections for brands, banners, packages and the gallery exist but do not appear until they have real content. The Gallery page honestly says it is in preparation. A map and Google ratings have not been added because the studio does not have a Google Business profile yet.

An admin the owners run themselves

The WordPress admin has a dedicated “Studio Lumière” menu, built for this studio rather than for salons in general:

  • Details and bookings: biographies, phone numbers, opening hours and business details for each specialist, the address, the online booking link (validated), photos and preparation instructions.
  • Monthly banners in two formats, with start and end dates. Headline, description and terms are real text, not just an image, so search engines and screen readers can read them.
  • Packages with a required total price and no automatic “savings” calculation. If an owner removes a service, every package containing it disappears from the site automatically, so an offer that no longer exists is never shown.
  • Brands and a gallery of hand-picked work with an optional link to the original Instagram post, without API tokens or automatic imports.
  • Changes show up immediately. When an owner publishes, edits or removes a banner, package, image or detail, the site clears the cache of the pages that show it. The same happens when a banner expires. No more “why can’t I see my change”.

All business data sits in the plugin rather than the theme, so it is preserved even if the design changes one day. It is the same approach we take in all our custom WordPress development.

Local SEO by search intent

We mapped keywords to what the person is looking for, not to how often a phrase is repeated (the searches themselves are in Serbian):

SearchPage
beauty studio / beauty salon Stara PazovaHome and About
lashes / facials Stara PazovaLashes and face
hairdresser / hair salon Stara PazovaHairdressing
  • Structured data describes the studio as a BeautySalon with two separate booking contacts. Opening hours and legal entities are never merged into one inaccurate record.
  • Technical hygiene: one H1 per page, self-referencing canonicals, all public pages in the sitemap, empty archives kept out of the index, and a real 404 for missing addresses instead of a redirect to the home page.
  • Controlled launch: the site was hidden from search engines during development, and indexing was switched on only after an SEO review and a technical check.
  • A favicon on a white background. The transparent icon looked fine on white but disappeared on dark browser themes and in search results.

We do not guarantee rankings. Search results and the effect on bookings have not been measured yet, because the site has only just launched.

Speed, accessibility and security

  • Lean code. No front-end framework, page builder or jQuery on public pages: PHP, plain CSS and four small JavaScript files. Fonts are local and the calendar loads only on request.
  • Caching that works with the site. Instead of turning WP Rocket optimisations off when something broke, we excluded the scripts needed for interaction from delay and protected the menu and booking-window styles from removal.
  • 0 automated WCAG A/AA violations on the reviewed pages and dialogs (axe). That is an automated check, not a certification, so manual keyboard testing was done alongside it. Tested at seven screen widths from 320 to 1440 pixels, in two browser engines.
  • Protection only where it is needed. reCAPTCHA protects the admin login, while public pages do not load it, so visitors carry no extra weight. If Google’s check is unavailable, the owners are not locked out; the event is logged instead. XML-RPC login is disabled.
  • API keys exist only in the server configuration, never in code or logs.

Process: 60 releases in five weeks

Between 6 September and 11 October 2026 we shipped 60 production releases. Each was archived with the previous version kept, so a rollback was ready for every release. Every deployment follows the same path: a backup, a code check on the server itself, the upload, a cache purge and a check of what is actually live, both through the public CDN and directly from the server.

For every significant change we ran automated browser checks (Playwright and axe): page status, a single H1, horizontal overflow, keyboard use, focus, reduced motion and working without JavaScript. We reviewed the results as screenshots on desktop and phone. The rule was simple: automated tests never book a real appointment and never send a message.

NumberWhat it means
2specialists, booking flows, sets of opening hours and legal entities under one brand
~5 weeksfrom the first to the last recorded production release
60production releases, each with the previous version kept
80/80booking-window checks at 1440, 390 and 320 px
0automated WCAG A/AA violations on the reviewed pages
11 + 10indexable pages and supporting SEO checks, each via the CDN and directly from the server
1SMS segment per reminder
0wrong SMS messages caused by the time-zone bug
0external font requests per visit

What is not finished yet

  • The gallery is ready and waiting for photos the owners approve.
  • Brands, monthly banners and packages are ready in the admin and will appear once the owners confirm real offers.
  • A map and ratings are waiting for a Google Business profile.
  • Service illustrations will be replaced with real photos.
  • The first meaningful search and booking data is expected only after a few weeks of operation.

What we learned

The easiest thing is a single “Book” button. But when two businesses stand behind one name, a single button sends half the clients to the wrong calendar and makes the other owner all but disappear from the site.

A reminder that arrives two hours late is worse than no reminder. That is why we verified the time zone with a real appointment before launch, not just against the documentation.

One letter can double the cost of an SMS. Any “č” or “š” cuts the allowed length from 160 to 70 characters. So the system counts before it sends.

An empty section is better than an invented offer. A site that has only just launched does not need to look as if it has everything. It needs to be accurate.

Running a salon, studio or practice with several specialists?

If different people in your business take bookings in different ways, your website should not hide that; it should send every client to the right place. We would be glad to look at how you work.

Call us now