Front Page Layout (sphynx)

DrEdition's front page layout engine is called Sphynx. Whenever you publish an update to your front in DrEdition, Sphynx generates markup and CSS for embedding directly on your own web site, and makes sure you at all times accomplish the best available representation of your design intention for the front page.

While we strongly recommend considering Sphynx for Front Pages, you may also use the API directly to fetch Front Page data from DrEdition.

Sphynx provides a real-time preview while editing

Design

Feel free to draw the whole front page in a graphical design tool of your choosing as a specification for the below.

  • Font type and color(s)

  • Minimum, default and maximum font sizes.

  • Background color(s).

  • List all placeholders needed (i.e. fixed or flexible advertisement positions, or any fixed or flexible widgets on the front page, i.e. videos or "most read"-list.)

Get in touch with support@aptoma.com to have a front page setup based on your design.

Layout

We support four layout breakpoints:

  • Small/mobile

  • Medium/tablet

  • Large/medium desktop

  • X-Large/wide desktop

There's an underlying 12 column grid, where you define the column gap and total width for each size. Within each size, the layout will be either responsive or adaptive. We strongly recommend using responsive for Small, and the X-Large should be fixed at your widest desired viewport width. Whether you use responsive or adaptive for Medium and Large is up to you. An adaptive layout will work better with fixed size ads or other non-responsive content, while the responsive layout will better fill the user's viewport.

Item Sizes

For Medium, Large and X-Large:

  • One third, four grid columns

  • Half, six grid columns

  • Two thirds, eight grid columns

  • Full, twelve grid columns

For Small:

  • Half, six grid columns

  • Full, twelve grid columns

Grouping

The layout engine will optimize the visual presentation of your front page according to the journalistic priority of your content. We also apply touch ups for a tight and effective design. Items are first grouped into rows or more complex compositions, and then the content of each group is optimized with font size and image crop adjustments.

Items can be assigned to groups either manually or automatically. For manual groups, you still have the option of leaving the group layout selection to the layout engine.

Front editors assign priority to items, either A+, A or B:

  • A+ and left aligned image will always be 100%

  • A-priority will be at least 50%

  • B-priority will be at most 50% (unless left-aligned image)

With these constraints in place, items are assigned to groups:

  • Manually created groups are left untouched

  • Items that need to be fullwidth (A+/placeholder/left-aligned image) will always be fullwidth

  • Items are not pulled up across groups/fullwidth items

  • Items may be reordered within an auto-group/row to accommodate priority/size restrictions

  • The layout engine is greedy, ie BBB will be a triple row, not BB plus B.

  • Items without image will have additional restrictions, to prevent size mismatch when placed next to items with image

Grouping is targeting desktop, as that's where the most complex layouts are supported. Group layouts have mostly deterministic tablet and mobile translations, but priority and placeholder grid restrictions may trigger alternate layouts. Get in touch with us for information about available layouts and how they behave.

Placeholders

Placeholders are what you will use to insert ads and any kind of dynamic editorial content on your front. For an item to be identified as a placeholder, it needs to have a property "placeholder". The actual value is up to you, but we recommend using this value to signify the placeholder type, ie. "ad" or "latest-news-widget".

Planning Placeholder Setup

When designing your front page, you should make a list of all placeholders that will be needed, including both ads and editorial widgets. For each placeholder, specify its rendered dimensions (including any "Advertisment" labels etc), and what restrictions apply regarding placement in the grid. Are any placeholders responsive or variably sized?

Once you have this list, we can help you configure the schemas in DrEdition optimally. We also recommend creating a feed of all placeholders, so that your editorial staff can easily place them on the front page without having to know all the technical details.

Placeholder Schema

Sphynx will optimize the editorial content around placeholders, by picking layouts and adjusting image crops. In order to help Sphynx make optimal choices, you should configure your placeholders with some device specific properties:

{
"placeholder": {
"title": "Placeholder type",
"format": "enumTitle",
"default": "ad",
"type": "string",
"oneOf": [
{
"title": "Ad",
"enum": [
"ad"
]
},
{
"title": "Latest news",
"enum": [
"latest-news"
]
}
]
},
"showOnMobile": {
"type": "boolean",
"title": "Show on mobile?",
"default": true
},
"mobileSize": {
"type": "string",
"format": "enumTitle",
"default": "300x250",
"x-schema-form": {
"condition": "vm.model.data.showOnMobile",
"destroyStrategy": "retain"
},
"enum": [
"300x250",
"320x160",
"160x160"
]
},
"mobileGrid": {
"type": "string",
"format": "enumTitle",
"default": "full",
"x-schema-form": {
"condition": "vm.model.data.showOnMobile",
"type": "radios",
"destroyStrategy": "retain"
},
"enum": [
"full",
"half"
]
},
"showOnTablet": {
"type": "boolean",
"title": "Show on tablet?",
"default": true
},
"tabletSize": {
"type": "string",
"format": "enumTitle",
"default": "300x250",
"x-schema-form": {
"condition": "vm.model.data.showOnTablet",
"destroyStrategy": "retain"
},
"enum": [
"740x60",
"300x250"
]
},
"tabletGrid": {
"type": "string",
"format": "enumTitle",
"default": "full",
"x-schema-form": {
"condition": "vm.model.data.showOnTablet",
"type": "radios",
"destroyStrategy": "retain"
},
"enum": [
"full",
"two-cols",
"half",
"third"
]
},
"showOnDesktop": {
"type": "boolean",
"title": "Show on desktop?",
"default": true
},
"desktopSize": {
"type": "string",
"format": "enumTitle",
"default": "300x250",
"x-schema-form": {
"condition": "vm.model.data.showOnDesktop",
"destroyStrategy": "retain"
},
"enum": [
"930x180",
"580x400",
"468x60",
"300x250"
]
},
"desktopGrid": {
"type": "string",
"format": "enumTitle",
"default": "full",
"x-schema-form": {
"condition": "vm.model.data.showOnDesktop",
"type": "radios",
"destroyStrategy": "retain"
},
"enum": [
"full",
"two-cols",
"half",
"third"
]
}
}

The grid settings while take precedence when creating layouts, while the size settings will affect the image crops of the other items in the same composition. Size should include any additional padding or labels of your placeholder content.

Placeholders with grid settings will at least get their required size, but may in certain cases be given more space (ie. due to next/previous item having fullwidth requirement)

Fetching Content

You can fetch urls to the latest published version of your front at https://sphynx.aptoma.no/burned/{editionId}.

You should download the content from the HTML url, and serve from your local cache. The CSS url can be hot-linked, but you may want to download this as well, or proxy through your own CDN.

The urls will be updated whenever their contents change. Ie. the CSS url will be same until the CSS has been updated.

Production images will be served from https://smooth-storage.aptoma.no/. If you do not want to incur extra traffic costs from Aptoma, we recommend that you set up your own image proxy and replace the image urls accordingly.

The front content will be wrapped in a single div.front-element.

Processing Placeholders

Any placeholders you add to your front will be rendered as dre-item--placeholder, with some additional data, including an attribute data-id, referencing the item in the edition. The Sphynx output is valid HTML, which can be loaded into a DOM parser for manipulation:

const dom = new JSDOM(html);
const doc = dom.window.document;
const edition = await getEditionFromDrEditionApi(editionId);
Array.from(doc.querySelectorAll('[data-placeholder]')).map((node) => {
const item = edition.items.find((item) => item.data.id === node.dataset.id);
const div = doc.createElement('div');
div.classList.add('dre-replaced');
div.innerHTML = myPlaceholderRenderer(item);
const parentNode = node.parentNode;
parentNode.replaceChild(div, node);
});

Placeholders rendered by Sphynx will get attributes data-lg, data-md and data-sm, as well as a style attribute that sets CSS variables --lg-width, --lg-height, --md-width, --md-height, --sm-width and --sm-height. These are used during measuring of items to optimize the layout. You should not rely on their presence to control item size after you have run your placeholder replacement logic, as hese are not supported by older browsers.

We recommend replacing the entire placeholder element with your own markup, making sure that you replace it with a single root element (as some layouts depend on the number of direct children). This will prevent placeholder styles that are only used for layout to bleed through to your production site.

Lazy Load Images

To enable lazy load of images on the published front, add the following properties to the edition schema:

{
"lazyLoad": {
"title": "Enable lazy load of images?",
"type": "boolean",
"default": false
},
"lazyLoadStartRow": {
"title": "First row to start lazy loading images",
"type": "number",
"default": 0,
"x-schema-form": {
"condition": "model.lazyLoad",
"destroyStrategy": "remove"
}
}
}

This will replace the src attribute of images starting at the specified row with data-src. The image will also get an additional lazy class.

Provided the browser supports IntersectionObserver (or has a polyfill), this should work:

document.addEventListener('DOMContentLoaded', () => {
const lazyImages = [].slice.call(document.querySelectorAll('img.lazy'));
if ('IntersectionObserver' in window) {
const lazyImageObserver = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImage.srcset = lazyImage.dataset.srcset;
lazyImage.classList.remove('lazy');
lazyImage.parentNode.querySelectorAll('source').forEach((source) => {
source.srcset = source.dataset.srcset;
});
lazyImageObserver.unobserve(lazyImage);
}
});
});
lazyImages.forEach((lazyImage) => {
lazyImageObserver.observe(lazyImage);
});
}
});

This example is based on https://developers.google.com/web/fundamentals/performance/lazy-loading-guidance/images-and-video/, which also contains code for fallback options using event handlers without a polyfill.