Publishing a Wine List That Stays Current
A wine list changes faster than a food menu and breaks more website layouts. How to structure, group, typeset and update one without a PDF or an afternoon.

A wine list is the hardest thing on a restaurant website to keep honest. It changes more often than the food menu, it is longer, it has more structure, and it is the one page where being out of date is not a cosmetic problem but a service problem: a guest orders something that has not been in the cellar since spring. Most restaurant sites solve this by giving up and linking a PDF. That is a guaranteed way to make the list both unreadable on a phone and permanently a little bit wrong.
This is how to build one that stays current, for a wine bar, a small-plates room, or any kitchen whose list is a real part of what it sells.
The real problem is the update, not the layout
Every wine list on the web is out of date. The question is by how much and how visibly.
Why lists rot faster than menus
A food menu changes on a schedule the kitchen controls. A wine list changes when a case runs out mid-service, when an allocation arrives, when a by-the-glass pour is replaced on a Thursday because the previous one finished. The changes are small, frequent, and initiated by whoever is on the floor rather than by whoever manages the website.
Any publishing workflow that requires a design step will lose this race. If updating a pour means opening a design file, exporting, and uploading, it will happen weekly at first and monthly by autumn. The list does not go wrong all at once; it drifts.
The test for a workable system
There is a simple test for whether a wine list publishing setup will survive: can the person who knows the wine changed make the change, in under two minutes, from their phone, without needing anyone else.
If the answer is no, the list will rot regardless of how good it looks on launch day. Everything below assumes that test has to pass.
Model the list the way you talk about it
Before any typesetting, decide what a wine list is made of. This is a content modelling question and it is the one that determines how painful every future change will be.
Groups first
Most lists have two or three levels of grouping, and the right ones are the ones the room already uses. A list organised by color then region matches how a guest asks. A list organised by style, crisp and mineral, textured and aromatic, matches how some rooms prefer to talk. A list organised by the order it was entered into the cellar system matches nothing a guest has ever thought.
The grouping is a decision about service, not about data. Take it from whoever runs the floor, then build the structure to match it, rather than exporting whatever the inventory system produces and hoping.
An entry is a small set of fields
A single entry is not a line of text. It is a handful of named fields, and being explicit about which ones exist is what makes the whole thing maintainable.
A typical entry has a producer, a wine name, a region or appellation, a vintage, and a price, plus optionally a grape, a format and a short tasting note. Some of those are optional in practice: a list that shows notes for its by-the-glass pours and not for its bottles is perfectly normal.
The reason to name the fields rather than storing a line of prose is that everything downstream depends on it. Sorting by producer requires a producer field. Grouping by region requires a region field. Producing a print version with prices right-aligned requires a price field. A line of text can do none of it.
Vintages are a commitment
Publishing a vintage is a promise that the vintage in the cellar matches. For a list that turns over quickly, keeping that promise is real work.
The honest options are to publish vintages and commit to the workflow that keeps them accurate, or to omit them and say so. What does not work is publishing them once and letting them age, because a wrong vintage on a website is read by a knowledgeable guest as carelessness about the thing you are selling.
By-the-glass: model it as a section
This is the single highest leverage structural decision on a wine list, and it is the one most systems get wrong.
Why a flag fails
The intuitive approach is to mark individual wines as available by the glass with a flag or an icon. It reads fine and it is a maintenance trap.
When the by-the-glass selection changes, someone has to find every wine currently flagged, clear the flags, find the new wines, and set them. On a list of any size that is a hunt, it is easy to leave one set, and the mistakes are invisible until a guest orders a glass of something that is not on pour.
Why a section works
Make by-the-glass its own section, with its own entries, at the top of the list where guests actually look for it. Changing the selection is then a single operation on a single short list: clear it, write the new one.
If a wine appears both by the glass and by the bottle, the duplication is a feature rather than a problem. The by-the-glass entry carries the glass price and the pour size; the bottle entry carries the bottle price. They are different products from the guest’s point of view, and modelling them as one entry with two prices produces a row that is harder to typeset and harder to update.
The same logic for other rotating sections
Anything that turns over on its own rhythm deserves its own section for the same reason: a cellar selection, a list of large formats, a small set of pairings that go with the current tasting menu. Each is a short list somebody can rewrite in one sitting.
Table or list
Once the model exists, the layout question becomes answerable.
When it is genuinely a table
The HTML specification describes the table element as representing data with more than one dimension, in the form of a table. The useful test is whether a reader compares values down a column.
A list where a reader scans for a vintage year across producers, or compares prices down a column to find something in a range, is genuinely two-dimensional, and a table is the correct element. It gives the reader column headers, it gives assistive technology the relationships between a cell and its header, and it is what the content actually is.
When it is a list
A list where each entry is read as a unit, producer then wine then region then price, and where nothing is compared column against column, is a list. Forcing it into a table gains nothing and costs a lot on a narrow screen, because a table with four columns at 320 pixels wide either scrolls sideways or crushes every column into two characters.
Most wine lists in chef-led rooms are lists rather than tables. They are read entry by entry, and the price sits at the end of the entry rather than in a column being compared.
The reflow constraint decides it in practice
WCAG’s reflow criterion requires content to be presentable without two-dimensional scrolling at a width of 320 CSS pixels, and explicitly notes that some content, including data tables, may require two dimensions to be usable.
That exemption is narrow and specific: it exists for content that genuinely needs it. A wine list rendered as a table purely because it looked tidy on a desktop is not covered by it, and will scroll sideways on a phone. If you can express the content as a list, the reflow problem disappears entirely rather than needing an exemption.
Typesetting a long list
A wine list is the longest continuous run of repeating small units on a restaurant website, which makes rhythm more important than on any other page.
Groups need to be findable, not just visible
A guest with a preference wants to jump. A guest browsing wants to read. Both are served by making each group a real, findable landmark rather than just a line of bigger text.
That means real headings in the document structure, which is what WCAG 1.3.1 is asking for when it requires that information, structure and relationships conveyed through presentation are also available programmatically. A heading styled to look like a heading but marked up as a styled paragraph is invisible to a reader navigating by headings, which is exactly the reader most likely to be navigating a four hundred entry list by headings.
It also means an index at the top on long lists. Six links to six groups costs one screen and saves a lot of scrolling.
Collapsing, done carefully
On a very long list, collapsing each group is a genuine improvement on a phone. Two rules keep it from becoming a problem.
Everything should be expandable, and nothing should be hidden from search. A collapsed section built with the native disclosure element keeps its content in the document, which means a reader using the browser’s own find function can still reach it.
And the default state should be considered rather than reflexive. A list of eight by-the-glass pours has no business being collapsed. A list of two hundred Burgundies probably should be.
The price, and the gap before it
Wine lists have the same leader-dot problem as food menus and a sharper version of it, because wine entries are longer and wrap more often.
The reliable pattern is the same: name and region on one side, price on the other, and a flexible spacer between them carrying dots as a background rather than as typed characters. When the entry is too long for one line, the spacer collapses and the price sits at the end of the wrapped block rather than stranded on a line of periods.
Do not center a long list
Centred text is fine for six items on a card and painful for two hundred, because a ragged left edge gives the eye nothing to return to. Long lists want a hard left edge, consistent indentation for any second line, and the price aligned right.
Structured data that cannot drift
Schema.org’s Menu type has a hasMenuSection property, and MenuSection can itself contain further sections and items. That nesting maps directly onto color, then region, then wine, with no contortion.
The value is not that a wine list will produce a rich result. It is that the machine-readable version of the list is generated from the same structured content the page renders, so the two cannot disagree. A restaurant that maintains its markup separately from its page will, within a season or two, be publishing a machine-readable list that contradicts the human-readable one.
The printed version, for free
Rooms still want a printed list, and the temptation is to keep a separate design file for it. That file is the thing that will disagree with the website.
Because the list is now structured content rather than a layout, a print stylesheet can produce a clean printed version from exactly the same source: same entries, same order, same prices, with web navigation removed, links resolved, and the type set for paper rather than for a screen. Whoever prints it prints the current list by definition, because there is only one list.
This is a small operational detail that turns out to matter more than it sounds. The most common reason a website list and a room list disagree is that they are two documents. Making them one document removes the failure mode rather than managing it.
Search, sort and filter: when they earn their place
Every long list eventually attracts a request for filters, and most of the time the request is worth resisting.
The cost of a filter nobody uses
A filter is a control the reader has to understand before it helps them. On a list of forty entries the reader can simply read, and a row of filter chips is an obstacle between them and the wine. On a list of four hundred, a filter is a genuine relief.
The threshold is roughly where a reader stops being willing to scroll the whole thing, which in practice is somewhere past a hundred entries. Below it, good grouping and a jump index beat any filter. Above it, a filter earns its place.
What to filter on
If a list does get filters, they should be the axes a guest actually chooses along, which is rarely the axes the inventory system stores. Guests choose by color, by a rough style, by a price band, and occasionally by region. They very rarely choose by importer, by bin number or by the year a wine was purchased.
Price banding deserves particular care. Bands that are honest about the list you actually carry are useful. Bands invented to look approachable, where the lowest band contains two wines, are worse than none.
Filters must not break without JavaScript
A filter is an enhancement. The list underneath it has to be complete and readable when the filter is not running, which means the entries are in the page from the start and the control hides some of them, rather than the control fetching the list. A wine list that renders empty when a script fails is a worse page than one with no filter at all.
The same rule covers the browser’s own find function. If a filter has hidden an entry, a reader searching the page for it will not find it, so any filtered view needs an obvious way back to the whole list.
Where wine lists meet the rest of the site
A wine list is rarely alone. How it connects to the food menu is a design decision with service consequences.
Pairings belong next to the menu they pair with
A tasting menu with a wine pairing is one product in the guest’s mind and two documents in most content systems. The page that shows the tasting menu should show the pairing alongside it, or link to it so directly that the guest never has to go looking.
Modelling the pairing as its own short section, attached to the current menu rather than buried in the main list, keeps it accurate: when the menu changes, the pairing section changes with it, and nobody has to remember to update a second page.
One page or several
A short list belongs on one page. A long one can reasonably split by color, which makes each page faster and gives you URLs specific enough to link to and to share.
Splitting has a cost worth naming: a guest who wants to scan the whole list now has to visit several pages, and the browser’s find function only searches the one they are on. If you split, keep a complete single-page version available and say where it is.
Never make the list a download
The last rule is the first one people break under pressure. When a list gets long and a deadline gets close, someone always suggests linking the PDF the room already uses.
That decision undoes every benefit above: the list becomes invisible to search, unreadable on a phone without pinching, impossible to keep in step with the room, and unavailable to a guest using a screen reader. If the list is genuinely too long to publish well, the answer is to publish it in groups, not to publish a file.
What this looks like day to day
A workable wine list setup, once built, runs like this.
Someone on the floor opens the list in the client portal from their phone. The by-the-glass section is six entries; they replace one. They save. The page is rebuilt and live within minutes, the structured data regenerates from the same entry, and the printable version reflects it the next time anyone prints.
A bottle runs out mid-service. They remove the entry, or mark it unavailable if you prefer the guest to see what you normally carry. The change takes under a minute and nobody opened a design file.
A new allocation arrives in March. They add four entries to the right region group. The group heading, the rhythm, the leader dots and the price alignment all come from the stylesheet, so the four new entries are set exactly like the four hundred that were already there, without anyone thinking about it.
That last point is the whole argument. A list built as structured content is still correctly set after two years of edits by five different people. A list built as a layout is a little worse after every one of them.
Sources
Frequently asked questions
Should a wine list be a table or a list on a web page?
It depends on whether the columns carry meaning. If a reader needs to compare the same attribute down a column, producer against producer, it is tabular data and belongs in a table. If each entry is a self-contained unit read top to bottom, it is a list, and forcing it into a table makes it harder to read on a phone.
How do you show by-the-glass wines that change weekly?
Model them as their own section rather than as a flag on individual bottles. A section can be emptied and refilled in one edit, while a flag scattered across a hundred entries has to be found and cleared one at a time, which is exactly why by-the-glass lists go stale on restaurant websites.
Does a wine list need vintages on the website?
Only if you can keep them accurate. A vintage stated on a website and not in the cellar is worse than no vintage at all, so the practical answer is to publish vintages when your updating workflow can keep up and to omit them when it cannot, rather than publishing them once and letting them rot.
How should a long wine list be organised on a phone?
By the same grouping you use at the table, with each group collapsible so the reader can skip to the one they want. A guest who drinks Riesling should not have to scroll through four hundred entries to find it, and a guest browsing should still be able to open everything.
Can a wine list be marked up as structured data?
Yes. Schema.org's Menu type accepts sections through hasMenuSection, and sections can nest, which maps cleanly onto the way a wine list is already organised by color, region or style. The markup should be generated from the same content the page renders so the two cannot drift apart.
Is a separate PDF of the wine list worth keeping for print?
Keep it for the room, not for the website. A print stylesheet can produce a clean printed version from the same structured content, which means the printed list and the website list can never disagree, and nobody has to remember to export a second file.
Want a site like the one described here? Book a demo with MenuWebStudio.