The Opus Website Framework
why you might want it, and how you would use it.
In this article, you will read about:
The Opus Framework - a free low-maintenance content creator's Website engine
Intended for artists, writers, photographers, videographers, audio engineers etc who want to spend their time creating content, not Webpages.
What is, and is not provided
And how to use this framework.
As a content creator, I don't want to spend my life managing my Website; I want the content I create to be the Website.
Having written a poem I don't want to then go into a WordPress™ dashboard; clone a template; cut'n'paste the text; fill out a load of metadata; and then publish the poem.
I just want to save the poem onto the Web server and have a new page appear on my site.
Or as a photographer, I want to be able to upload a bunch of images from my camera direct to my Web server and have a new gallery appear on my Website.
So I wrote the Opus Framework - a bunch of software code modules that create my Website directly from the content I put on it; zero content management system (CMS) overhead.
To be fair Opus is near-zeroCMS, but I think pretty good. There's a bunch of stuff to get your head around, or come to terms with - it may not seem very 'natural' at first, but the ongoing operation of the Website becomes ultimately very easy. The vast majority of small, independent Websites become stale and out-of-date very quickly precisely because of just how much effort owning a Website demands. At least with Opus, all that effort is up-front. Once you're up and running the Website takes care of itself. Or rather, the content you are creating anyway, takes care of the Website for you.
Highlights and Features
Rich Content Pages
Allows presentation of text, image, audio or video creations in full context, not just 'promoting work' but telling the full story.Editorial grouping of works
Allows online versions of chapbooks, albums, or other collated works. Meaningful browsing for the audience.Media Playlists
Allows the creation of a 'lean back' experience for the audience with tracks or videos playing automatically in sequence.Full-screen Galleries
For an image-led experience galleries can be created that allow slideshow playout in fullscreen.Accessibility Features
Opus is based on the principle that equality is indivisible - so-called 'accessibility features' (keyboard shortcuts, screen reader prompts, semantic HTML, colour contrasts, image alt texts and audio transcripts) create a better experience for everyone and are not an afterthought or 'special case'.Parental Guidance
In the Wild West of the Word Wide Web, it's impossible to know who your audience is (as opposed to who you want them to be!) - so the Opus framework supports your freedom of speech by ensuring consent through NSFW sign-posting.Part Works
Not everything on your Website will be a fully crafted artistic 'work'. Perhaps there will be news bulletins, or 'promotions' to off-site offerings (an e-shop maybe). Opus 'part works' (or 'promos') can be easily added.Quality Syndication
Links shared from an Opus Website are fully adorned with all the needful 'metadata', so they show up beautifully in search results and on social media platforms.Low Maintenance
The Opus Framework avoids the need for complex and costly 'Content Management Systems'. Your work IS the content, you can add it in or take it out easily.High Security
Opus is fully compliant with the latest (v3) Content Security Protocol with very few holes drilled through it (eg. no unsafe inline script source), meaning that Opus-powered Websites are less vulnerable to attack than most.High Performance
With aggressive caching, NextGen image formats and device-sensitive resolutions Opus Websites deliver content fast.Open and Adaptable
The Opus Framework is written in the popular PHP language which currently powers around three-quarters of all Websites and all of the code (7,500 lines of it!) is included in plain text. So not only can it be readily adapted, but learning to do so is a valuable effort. There are also plenty of 'injection points' so the framework can be extended without getting elbow-deep into the core code (unless you want to).Interoperable
As well as supporting quality syndication, comprehensive meta-tags ensure Opus framework Websites 'play nice' in the cyber world. Andoid™ and Apple™ specific icons are supported and the site is fully site mapped for robots and search engines.100% Free
It just is. No paying extra for 'widgets' and no ongoing costs for the right to be abused by 'templates' and 'widgets'.
Limits and Boundaries
It's a Website!
The Opus Framework creates a Website. It does not create:
An e-commerce shop
A social media platform
A streaming service
A bulletin board, a forum, or a discussion platform
Over the years (nearly two decades) I have experimented with adding all these kinds of features to my Website; only to find that they are little used, largely abused, and horribly inefficient. But more than that, these kinds of features never achieved their purpose. When I shared a link people would comment on the link on a social media platform, but wouldn't use the in-page comment facility. I could get decent sales in my e-commerce shops (eg. RedBubble), but rarely via the 'buy it now' links I littered throughout my Website. People would just prefer to go to 'a shop' to buy my work than to buy it off of my site.
Sure, the experience may be very different for 'business' (organisations with aggressive marketing budgets) - but for the independent creator it makes the greatest sense to separate the concerns. Sure I sell stuff, but I'm not a capitalist, 'sales' are a very minor part of my identity. I prefer to keep that tawdry business walled-up in my e-commerce platforms; allowing my Website to become an undiluted pure 'avatar of my creative self'. I have accounts on The Socials where I can promote my work and ask folk to come to my Website to enjoy more of it - but I see no (or little) value in promoting those platforms on my Website.
The Website is to be an immersive place of carefully curated and loved creative content. Diluting it with the facebook comments module (or BUY-it-now! widgets) is just unhelpful and a little bit 'icky'; creepy even.
So Opus doesn't do everything you might want to do online. It creates a Website that presents your best self, leaving you free to use other Internet services (like The Socials) however you so desire.
The Template
Mass-market web design solutions (Like Wix™, WordPress™...) tempt you with endless 'templates' and 'widgets' trying to up-sell that £1/month or £20 one-off feature. You want to be 'distinctive' and for the right price, you can certainly seem to be.
But your authentic, distinctive voice is carried by the content, not the dressing it is given.
The main effort of the Opus Framework lies in its features that allow truly rich content to be displayed. But the menu bar, the sidebar, the footer, all the bare mechanics of a Webpage are really neither here-not-there. Things like the dreaded infinity-scroll, or the latest whizz-bang-GOTCHA widget won't increase engagement with your content. The words and images you use do that.
So Opus uses a single, simple, template; which is quite enough. You can have as many sections as you like, as many pages in a section as you like, as many collections or tags as you like, and you can use whatever colours or fonts you want. You can even embed widgets if you must (like the BandCamp™ widget on my page dedicated to the excellent Werbeniuk).
You can't have different styles of galleries, or video playlist players. You can't vary the number of columns on a page. In Opus, the content layout is completely flexible, but the page mechanics and page layout are not. In Opus, the single template liberates the content.
Audience Respect
The Opus Framework respects your audience.
No other web design platform puts the handling of mixed-sensitivity content (NSFW versus 'prurient' content) at its heart. But more than this, Opus respects your audiences' privacy.
I have previously integrated facebook comments and likes, twitter timelines, and Google Analytics. You know, like a modern Website is supposed to. Or so we are told. Between them, these things meant my Webpages would load in 5-10s... My Webpages now load (without any of those things), on desktop, in around 1 second. All these 'services' added enormous bloat, detracting from the Website experience overall, for no real useful purpose other than turning my Website into a digital spy. So they are gone, and site visitors are not under Big Tech surveillance. [It is possible to still use the twitter feed if you want slow and creepy Webpages].
PayPal can also be integrated (for donate actions), but that can be suppressed. So it is quite possible, with the Opus Framework, to completely disconnect from Big Tech and to create a truly safe and respectful place for your audience to visit. In itself a feature, which the likes of WordPress™, would never embrace.
The Opus Framework powers Websites for content creators. To adopt the framework it is necessary to have a base understanding of how it hangs together. Some of the following may seem redundant, or (hopefully) over-simplified. It's a starting point:
In summary, your Website is a small fraction of the World Wide Web which is but a part of the Cyberspace facilitated by the physical Internet. Your Website provides a number of web strands, or URLs. Your Web Server matches these to the 'real files' and data you have supplied via FTP to create the HTML documents of your site.
Those HTML documents present one or more impressions of some greater work, which is in fact a fragment of your imagination as artist. They are published with the same care, diligence and permanency of any creative endeavour.
To adopt the framework there are also a number of Crital Concerns that must be heeded. Essentially the rules of the game, things that must never be forgotten:
Adopting Opus
So if you never quite got going with WordPress™, or the novelty of Wix paled especially as the costs started adding up, you might be ready to actually create a Website. Instead of pretending it's easy in order to sucker you and lock you in, Opus demands you learn some basics, then provides the tools to actually make operating the site straightforward. Perhaps a high bar to get started, but a framework that lets you fly once you're up and running. Every page on this site is generated by the Opus Framework, and it has some pretty neat features; both in what you see and behind the scenes. And it is totally open and transparent, letting you learn as much or as little as you care to.
Setting-up
You'll need a public-facing Web server, of course, you will want it to be an Apache server running PHP 8+ (if that means nothing, just tell your provider!). I investigated free options but there was nothing really satisfactory. I used to pay FastHosts™ to serve my site but they became more and more corporate penny-pinchers over the years so that now (IMHO) they are the very definition of bad Web citizens (I can't believe what they charge, on top of everything else, for an SSL certificate; which by rights should be free). I wouldn't touch the likes of Amazon Web Services because, you know, well AMAZON. So I bought local, a small outfit with a guy who'll pop round my place if needs be. The kind of service Big Tech could never deliver.
All you need to do to get Opus working is
copy the 'Framework' directory (in the downloadable Zip file, see bottom of this page) into a directory called 'Framework' on your Web server's public HTML folder
copy all the files from /framework/root-stuff up into the same public HTML folder
create the site section directories
edit the site-config.php file now found in the public html folder
After which you're ready to start creating content for your Website.
The first 2 steps are straightforward and your Web server supplier can probably help you achieve it if it all seems too unfamiliar. OR if you really are using Opus I'll be delighted and you are free to drop me a line for help - I might have to edit this promise if I'm suddenly inundated, but I don't realistically expect that's gonna happen...
The Content Directories
Create a new directory inside your public HTML directory for each site section. This is where the section content will be uploaded. Come up with a naming scheme for them. My directories are named using the pattern 'what you see in the URL plus the year I last overhauled all of my content'. So pages in the Words section of my site get stored in the directory words2022. Note that I reflect what the URL looks like in my directory naming scheme, but I don't totally imitate it; my directory isn't just called 'words', that would be bad, it would confuse me and possibly the Web server itself.
By the way, if you want to create a site section that doesn't appear in the Menu, use a hyphen (or minus sign) at the start of the name.
And if everything that goes in that section is considered to be NSFW, add '-nsfw' to the end of the directory name.
Each content directory requires 6 sub-directories to be created with the precise names as shown - all 6 must exist and must be correctly named or else things will look very, very BAD. It's completely optional whether or not you use these sub-directories (but you should), however they must exist.
The content sub-directories:
authornotes: additional text files with information about the work.
collections: text list and image files that create editorial colelctions of works.
images: where the vast majority of images (jpg) get stored for inclusion in the works. In fact all images except the lead images for groupings (collections and tags).
keywords: very simple, one-line, text files that allow keywords to be created for works. These are the only text files in Opus that do not start with a title. Allegedly keywords are of little importance these days because the capitalists ruined the idea by stuffing all kinds of meaningless advertising rubbish into page keywords to such an extent that search engines just ignore them now. It's awful really to think that such a beautifully simple semantic concern could be so sullied, but that's the world we live in. Don't feel you have to bother with these, I rarely do. The facility is there to use though, just in case the world ever gets fixed.
media: where all audio (mp3) and video (mp4) media files get stored, along with any partners they may have.
tags: text list and image files that create simple 'tag' groupings of works.
If you want to create a Webpage you will just upload a text file into a content directory. You will upload additional files in the content sub-directories if you want to further enrich that Webpage.
A bit more about files:
Base Name: the filename given to a work without its extension. A work is stored in a text file named something like my-work.txt. The base name then is my-work. The base name appears in the URL and is used to link additional assets to the work. E.g. there will be an image (my-work.jpg) in the images directory.
Text files: all text files (except keywords) start with a title.
List files: allow works to be grouped, with the name of each work in the group listed line-by-line following the title. The list ends either at the end of the file (tag lists) or when the separator '@@@' is entered (collection files) - after which the collection description can be written.
Lead images: when you syndicate an Opus Webpage (eg. share a link on facebook, or see a search result) it helps if there is an associated image. Not only does that make the link stand out, but once people click the link and see the image again on the page there is strong visual feedback that they have arrived where they expected to. So every work in a section should have an associated image (with the same base name) stored in the images content sub-directory.
Partner files: enriched works can include additional 'assets': embedded images or galleries of images and image albums; additional rich text files for the expandable 'accordions'; individual audio or video files, or 'playlists' of several such media files. These assets can have 'partner' images or text files - so if you want a cover image to display when an audio track in a playlist is played just save a jpg in the same place as the mp3, with the same base name. Or, if the default title for an asset (taken from its filename) isn't good enough, store a text file with the asset giving a full title as the first line.
Rich Text Files: as discussed earlier Opus uses 'Lazy HTML' text files which makes them quite flexible. But you can also use special 'macros' (see later, in 'Enriching a work') that allow embedding of assets and rich/complex layouts to be easily added.
[Note you can supply a poster.jpg file in a media asset's directory. All media assets that do not have a partner image file will partner with this poster instead. This is primarily intended for video playlists that only require one poster image (and do not need images for every video]
The site-config.php file
Once you have created all of your content directories (and their sub-directories) the last step in setting your site up is to configure the site-config.php file in the public HTML folder of your Web server.
Here's what my config file looks like:
$GLOBALS ['CACHE_BUSTER'] = '2.023.3';
$GLOBALS ['TWITTER_HANDLE'] = 'antsmithpoet';
$GLOBALS ['SITE_NAME'] = 'Ant Smith';
$GLOBALS ['INFORMAL_NAME'] = 'Ant';
$GLOBALS ['FORMAL_NAME'] = 'Ant Smith';
$GLOBALS ['DONATE_ACCOUNT'] = 'TheGameCat';
$GLOBALS ['SITE_EMAIL'] = 'enquiries@antsmith.net';
$GLOBALS ['SITE_ADDRESS'] = 'antsmith.net';
$GLOBALS ['SITE_PROTOCOL'] = 'https';
$GLOBALS ['FG'] = '#101820';
$GLOBALS ['BG'] = '#fafffa';
$GLOBALS ['LINK_HOVER'] = '#e60000';
$GLOBALS ['SITE_SECTIONS'] = array (
array (
'Name' => 'Home',
'URL_Part' => '',
'Branch' => 'home2022',
'Livery' => 'rgba(112,112,112,1.0)' ),
array (
'Name' => 'Words',
'URL_Part' => 'words',
'Branch' => 'words2022',
'Livery' => 'rgba(255, 69, 33, 1.0)' ),
array (
'Name' => 'Photography',
'URL_Part' => 'photography',
'Branch' => 'photos2022',
'Livery' => 'rgba(255, 166, 5, 1.0)' ),
array (
'Name' => 'Articles',
'URL_Part' => 'articles',
'Branch' => 'articles2022',
'Livery' => 'rgba( 2, 166, 236, 1.0)' ),
array (
'Name' => 'Gigs',
'URL_Part' => 'gigs',
'Branch' => 'gigs2022',
'Livery' => 'rgba(245, 184, 217, 1.0)' ),
array (
'Name' => 'Policies',
'URL_Part' => 'policies',
'Branch' => '-policies2020',
'Livery' => '#fff' )
);
This is an actual PHP script file, so you have to be ultra careful when changing it - especially regards things like commas, quotes, semicolons and brackets. But at least once you've successfully edited it you can count yourself as a developer, since this is a fragment of software code!
This might just be the scariest bit of setting Opus up!
The file is in 2 parts. At the top there are 12 individual configuration items, followed by a set of 4 configuration items for each of the site sections.
The individual configuration items are:
CACHE_BUSTER: all Opus assets are given a very long cache life (about 1 year) so that re-visits to a page are fast, even for infrequent visitors. Which means if you change an asset (re-encode a video perhaps) you can't be sure if returning visitors will see the updated file, or if they will use the version stored in their local browser cache; that's how the Internet works! If you change an asset soon after you first publish it you can refresh your own browser to see the change, and you don't have to worry about anyone else as they probably haven't visited the page with the asset on it yet. But if you change an asset on an older page you will want to be sure that returning visitors see the change. This is what the cache buster does. Change its value and every visitor to your site will fetch a fresh copy of all assets, as though they had never visited before. It's a bit of a hammer to crack a nut, but it does the job!
TWITTER_HANDLE: if you want to include your twitter timeline in a 'promo' (see Non-Works, later) you will need to supply your twitter handle here; otherwise just use the 'empty' string ('') for this.
SITE_NAME: This appears in the banner, and the metadata, of all pages on the site.
INFORMAL_NAME: used in the 'contact' box at the bottom of every page.
FORMAL_NAME: used in every page's metadata to declare the author of the page.
DONATE_ACCOUNT: a 'PayPal.me' account that can receive funds.
SITE_EMAIL: used in the contact panel at the bottom of every page.
SITE_ADDRESS: used when generating external links to your Website (eg. in the share buttons at the bottom of every page). I guess I've been a tad lazy as this could be worked out dynamically!
SITE_PROTOCOL: indicates if external addresses should be generated as HTTP or HTTPS (again I should get rid of this and work it out for you..!)
FG: the default foreground (text) colour for the site.
BG: and the default background colour for the site.
LINK_HOVER: the default colour for links when they are hovered over by the mouse cursor.
A note on colours
To see how colours are declared refer to https://www.w3schools.com/html/html_colors.asp
Currently, Opus only allows hex, rgb or rgba colour values: HSL, HSLA and Colour Names cannot be used. I might change that one day, especially if anyone were to ever ask me to...
The rest of the site-config.php file defines 4 configuration items for each site section in turn.
Name: this is the human=driendly name for the site section, used in titles and H1s for the pages - and in the menu.
URL_Part: this is how the section is accessed by a web address. Once created you should never change this because URLs are sacrosanct!
Branch: this is how the disk-based content directory is wired to the URL. If you ever need to move your content to a different directory (perhaps when undergoing a major overhaul of your content) you can do so without upsetting the web addresses (the URL_Parts). Notice how the legally required policies live in their own section (the last section) but do not need to appear in the menu.
Livery: each section of the site is differentiated by distinct livery colours, which are declared here.
Another note on colours
You should make sure there is good contrast between your foreground and background colours for the site. Sometimes Opus will write text on top of the Livery colour (panel headings). This text is also written in the foreground colour declared for the site, unless that would result in a poor contrast ratio, in which case Opus uses the declared background colour. This is why you might sometimes see panel headings in the background, not the foreground, colour.
All of the foregoing may be very unfamiliar, but that is everything needed to establish an Opus-powered Website. Next, you can start creating Webpages...
Creating a work
All works start life as a text file uploaded into the content directory of the appropriate site section - the existence of such causes a Webpage to become available. The address of the Webpage is the URL_Part of the site section plus the base name of the text file.
That in itself is enough to make a page, such as this one: https://antsmith.net/words/the-nameless
However, ideally, all works should have an associated lead image. If you look at the above page you'll see there is no image on the page at all as in this case I have been a tad lazy and I've failed to follow my own rules! If I share this page this is what it looks like on facebook and twitter:
When syndicating Opus will always find some image to use. In this case it has used the default image for this section, which is called words.jpg (the filename is the section's URL_Part) stored in the images sub-directory. This helps, but if I have a lot of works without lead images syndicated links will look very repetitive, so it's really best to always supply a specific lead image. Even if I don't always do that! (I will one day).
This means when you create a section, you ought to add a section default image into the images sub-directory.
And if you don't have a default image for the section Opus will use the default image for the site, which is called me.jpg and can be found in the site's root folder (the public HTML folder). You can of course make your own 'me.jpg' file.
Notice also how the description for the syndicated link is the first few lines from the work itself, you can read the start of the poem before clicking through.
More details
The above page is fair enough, it gets the poem 'out there' for people to read with really very little effort (given I'd already typed the poem into a file). But there must be billions of poems in the world so a little page like this isn't very compelling, isn't likely to be discovered, and if it is people aren't likely to spend more than a few seconds with it. You can't expect much effort from people who find your work if you haven't spent much effort putting it there!
Although, sure, a creative work ought to speak for itself it isn't actually good enough in the world of publishing to expect it to do so. There's just too many other good creative works out there. More detail, more context, and more effort is needed if you want others to give you their time and attention.
So a better example is this thoughtful piece following a story about a teenage suicide: https://antsmith.net/words/kys
The poem is in the file kys.txt in the content directory. The newspaper transcript is in the file authornotes/kys.txt. Two files to create, and two uploads to do (3 files if you do the right thing and add a lead image), but the result has a much greater impact and resonance. Also note that if this page is syndicated the link description will come from the authornotes file instead of from the work itself.
More Versions
For the poem https://antsmith.net/words/devotee I went all out and uploaded FIVE different files! The poem itself (devotee.txt); its lead image (images/devotee.jpg); supplemental notes about the poem's first broadcast (authornotes/devotee.txt); a video of the first broadcast (media/devotee.mp4) and an audio track collaboration version with musicians (media/devotee.mp3).
It's a very complete offer, certainly a page worth visiting and spending time with. I had the media files anyway as a result of projects I had been involved with. Not all pages will be so rich, but it is a very simple matter to make them so if you do happen to have the assets.
Enriching a work
So basic Opus Webpages can become quite rich just by putting assets in the right places.
But what if you want to make more of the video version by showing it in a large player at the top of the page (instead of in a small player to the side of the page)? Or if you have more than one video to include? Or multiple audio files? Or a whole collection of assets that represent not just a work but a project that perhaps took years to create, such as my London Underground project: https://antsmith.net/photography/269-the-project with all of its associated media coverage and audience stories etc?
In these cases you need to create 'richer pages', and that is done by using the 'macro' features of Opus text files.
The macros offer 2 main features:
Complex text styling and layout
ul: creates an unordered list - that is a bulleted, rather than a numbered, list.
ol: creates an ordered list
quote: creates a quotation with a citation
table: for laying information out as a table. Alternate rows are differently coloured to aid legibility and any row can be clicked to see the data in that row pivoted and enlarged which helps when there's a lot of columns in the table.
group: groups content together as a block which can then be centred, or around which other content can flow
literal: exactly displays the contained content - eg. allows a block of HTML to be displayed rather than interpreted by the browser; also allows macro-type text to be displayed rather than interpreted by Opus.
chorus: allows a repetitive chunk of text to be declared and then repeated where needed; eg for a recurring refrain in a lyric. Can be clicked to toggle between showing the full refrains or showing just the markers.
Inclusion of additional assets
img: adds an image to a page. The image can be clicked to open a fullscreen lightbox, or else can have an associated link to be followed on click, or else can handle NSFW images by covering them over until they are clicked. Works with the Opus image service to deliver highly optimised images of the right size for the visitor's browsing device (e.g. images delivered to mobile are much smaller than those delivered to desktop.
callout: creates a small panel in the section's livery colour with a heading, an image and descriptive text around which other content can flow.
audio-player: inserts a media player for one or more audio (mp3) tracks, including display of cover art.
video-player: inserts a media player for one or more videos (mp4), including display of an associated poster image.
accordion: inserts a set of additional rich-text files in an accordion-type device (e.g. the Key Concepts shown earlier).
gallery: insets a panel of several images that can be clicked to open fullscreen in a lightbox with a slideshow facility. May include also albums of further sets of images. Allows individual images to be 'liked' and can show a full-text description to accompany the image including any EXIF metadata stored in the image. Also uses the Opus image server to ensure images are highly optimised.
The first set of macros really just makes entering structured text (like lists, and tables) a lot easier than writing pure HTML. If you already know HTML you may prefer to stick with that than using these macros as that means your source files will be more transportable - ie. usable without the Opus framework at some future time. Personally, I find HTML lists and tables extremely irritating and long-winded so I use these macros all the time.
The second set of macros work with the Opus Javascript and adds a lot of interactive functionality. Theoretically, you can do these things yourself with pure HTML, but if you have that level of ability you're really unlikely to be wanting to use somebody else's Website framework anyway!
Anatomy of a macro
Macros are small nuggets of information that tell Opus how to achieve all of the above. They interrupt the normal flow of the text with special macro-start/macro-end signifiers ('###') and they look like this:
###{macro type} {optional set of extra styling directives}
{0 or more plain text definition lines, depending on type}
{0 or more further rich-text lines, depending on type}
###The callout macro has been used to create this panel.
It looks like this:
###callout right
Example Macro
./images/interview-with-a-pariah
Alt: Example image for a callout
The callout macro...
###
{macro type} is exactly as per the list just above (e.g. ul, ol, table,img etc...).
The 'extra styling directives' are things like left or right to place the macro content to the side of the page (with other content flowing around it). Or, center fit to centre align the macro content. outline will put the macro content into a white box with a black border. numeric would cause an ordered list to use numbers instead of the default 'letters' for each list entry. [ For those 'in the know' these are actually just CSS classes that have been defined in the Opus common-css file. additional styling directives can be added to the domain.css file in the public HTML folder if desired].
Most macros (not group or chorus) start with a number of plain-text definitions. In fact the additional asset macros only contain plain-text definitions. Following these definitions, the styling and layout macros then include further rich-text lines; the lines to which the styling and layout of the macro are applied.
This means that these macros can include further, embedded, macros - you can create a list of lists. Or you can put images inside of tables, etc...
Lists, quotes, tables, groups, choruses and callouts all allow further rich-text lines to be included within them.
Note, chorus is a kind of special case... Only the first chorus macro should include the rich text of the chorus. A later, repetition, of the chorus should just be an empty chorus macro (e.g. ###chorus###) since it is just a marker of the repetition.
The number of definition lines, and their meanings, depend on the macro type:
| type | No. | Meaning |
|---|---|---|
| ul,ol | 1 | List header, a line of text preceding the list. Can be a blank line |
| quote | 1 | The citation for the quotation, can be blank |
| table | any | tables are complex, check the deep dive (later) for details |
| group | 0 | doesn't need any definition, just groups whatever rich text is placed inside |
| literal | any | every line inside a literal is treated as simple plain text |
| chorus | 0 | doesn't need any definition, the rich text inside the macro becomes the chorus |
| img | 4 | file reference of the image alt text image shape associated link Only the first is required, others can be blank or missing |
| callout | 3 | Title file reference of the image alt text |
| audio-player video-player | any | a list of file references to media files of the appropriate type |
| accordion | any | a list of file references to text files to be included in the accordion |
| gallery | any | a list of file references to image files, interspersed with album definitions as needed |
Storing all the additional assets
My Photographic Skill photography page contains 2 separate galleries.
One (book-images) has a number of separate albums, whilst the other (sample) just has images without any deeper albums.
The directory structure I have used reflects this use of the assets on the page.
The asset macros all need one or more actual asset files to work with. If you place your assets in the 'right' place then you will find it easy to create references to them for the macros.
There is a default sub-directory for each type of asset, you've already met these when dealing with the 'lead' assets for a work.
| Asset Type | Default Directory |
|---|---|
| txt | authornotes |
| jpg | images |
| mp3 mp4 | media |
For very rich works you will find you have:
Incidental Assets: a whole bunch of one-off incidental assets (especially images) that you want to use throughout the work, and
Asset Sets:Sets of somehow related assets that you want to use together; perhaps as an album in a gallery or a playlist of audio tracks.
When you have additional assets for a work create a new sub-directory in the asset type's default directory using the base name of the work. Store your incidentals here. Also, create further sub-directories here for your asset sets; it doesn't matter so much what you call these asset set sub-directories, so long as you can remember the names when you come to use the assets. The assets themselves (either the incidentals or those in sets) should be given meaningful names, so you can avoid the need to create partner text files (for titling them) as much as possible.
You can make the asset set sub-directories as deep as you like. This is especially useful for galleries with albums. If I have 2 galleries on a page I might make a sub-directory for each and then create extra sub-directories within those for each album in each gallery, for example. It probably sounds like things are getting complex, but it generally makes sense when you think about what the assets are for and how they will be used.
Image Shapes
Image Shapes look like this:
1920w = scale the image to 1920 pixels wide
1920,1.7778 = fit image to 16:9 scaled to 1920 pixels wide
1080h,-1.7778 = crop image to 16:9 scaled to 1080 pixels high
OR, in the general case:
[[{px}[h|w]][,[[-]{aspect ratio}]]]
where:
[] = optionality
{px} = number of pixels
| = OR
h,w = to indicate if {px} designates height or (the default) width
[-]{aspect ratio} = required shape (w/h) of the returned image. Source is curtained to fit by default; will be cropped to fit for -ve values.
If nothing is provided (line is blank or missing in the img macro definition) then the image is retrieved at a default size (currently 320px width) in its native aspect ratio.
So: an image can be requested at a given width in pixels (e.g. 320, or 320w), the source will be scaled to that width with the height proportionally scaled. Alternatively, an image can be requested at a given height: e.g. 1080h; with the width being proportionally scaled.
Or: an image can be scaled and fit to a given aspect ratio. E.g. 1920w,1.7778 - 1920 pixels wide at an aspect ration of 16:9. Alternatively, a negative aspect ratio will crop the source image.
It's pretty flexible but looks complex maybes. In the simplest case just tell the macro how wide the image should be.
File References
There are two main concerns when it comes to referencing files:
Resillience: if I make, even a small, change to my disk structure will my file references break?
Reachability: can the kind of reference I am using even reach the file I want?
File references end up being littered throughout the works, and there are all kinds of reasons why you might end up moving bunches of files around. What you don't want, every time a file moves or a name changes, is to find that you have a lot of broken file references in your site. This is a classic Internet problem where you see severely broken sites throwing up 404 errors, or displaying blank boxes where images are supposed to be.
Wherever possible we want to use references that are relative to the context.
For example, I could reach the 'Deep Asset' callout image (as used above) with the reference:
../articles2022/images/opus-framework/asset-structure
But that's not at all resilient. Any change to my content structure means I would have to also edit the text file for this article and update that reference.
Luckily(!) Opus already knows that this article lives in the 'articles2022' directory (because that's where Opus found it) and it knows that the article's base name is 'opus-framework' (because that's the file Opus is processing when it found this reference).
So we can instead reference that same image like this:
asset-structure
Which is way easier to type in when I'm creating the work, reducing the chance of a typo screwing up my reference. And this is highly resilient. Whatever else might happen I only have to change this reference if I happen to change the name of the incidental image file.
This works because I put the image in the 'right' place. So why would we ever use the first kind of reference? Ideally, we shouldn't. But everything on your Web server should be reachable, you know, just in case. They're your assets, if they're up there you ought to be able to reach them!
There are two basic types of reference:
absolute with the ability to reach absolutely any file:
in site: e.g. ../articles2022/images/opus-framework/asset-structure
in section: e.g. ./images/opus-framework/asset-structure
relative with strong resilience to change:
to current work, in current section: e.g. asset-structure
to some work, in current section: e.g. opus-framework,asset-structure
to some work, in some section: e.g. articles,opus-framework,asset-structure
Relative references can reach any asset stored in the default asset directories of any work in any section. I.e. works can borrow assets from other works. This can be useful when building playlists and galleries. Sometimes. [Note though, relative references cannot reach asset partner files. If you wanted, say, an accordion of image descriptions you would have to use absolute section references, because image descriptions are stored in text files in the images directory, not in the default directory for text assets! This is kind of a fine point, don't worry over much about it, but it helps to illustrate why we have absolute references available.]
Important Note ABout References
You never include the file extension (jpg, txt, mp3 or mp4) in a file reference. Opus knows what extension to use as it knows what it is looking for!
Aggregating References
The foregoing examples all target a specific individual file, and that's kinda good enough because if we want a whole bunch of tracks in a playlist we can list each track one by one within the macro (same goes for videos, accordions and galleries). But it is a kind of pain and it means if we add an extra track (by uploading a new mp3) we have to edit the work to reference that track.
This is why we created additional, asset set, sub-directories.
Looking again at Photographic Skill, I can borrow a bunch of images from there to make a gallery here in this article, as so:
These are the 8 images from the 'sensor' chapter of the book, but I didn't have to reference each image separately. I just pointed Opus in the right general direction and it found all the assets of the right type. Here's what the macro for the above gallery looks like:
###gallery
photography,photographic-skill,book-images/sensor
###
Opus knows I'm looking for images (it's a gallery macro after all) and it knows I have referenced not a file, but a directory - so it brings back all the images it finds in that directory.
Now, if I want, I can extend the Webpage content simply by uploading more image files to the 'sensor' directory without having to re-edit the work's text file to include the new images. This means a Webpage can be pre-prepared and then content added by simple upload while 'out in the field', if you wanted to do that.
All references will 'auto-aggregate' like this if you happen to target a directory instead of a specific file.
Gallery Albums
Let's say we wanted to borrow the images from both 'sensor' and 'light' chapters of my book Photographic Skill to put into a gallery here. Let's add the lead image as well. We might use a macro like so:
###gallery
photography,photographic-skill,
photography,photographic-skill,book-images/sensor
photography,photographic-skill,book-images/light
###
Which would give us:
So great, we did get all the images, but it's a flat gallery. We can't see that they came from different places. You might have thought Opus would have arranged them into albums for us, based on the asset set directories we referenced. And it used to do that. But I found it became quite difficult to be in precise control of the gallery arrangement, especially when borrowing individual images from here and there.
So if you want albums in your galleries you have to tell Opus how to arrange the images it finds by adding gallery definitions. Something like this:
###gallery
photography,photographic-skill,
@sensor,Sensor Chapter
photography,photographic-skill,book-images/sensor
@light
photography,photographic-skill,book-images/light
###
Which would give us:
You can see an album has been created for each chapter. The cover image didn't go into an album because I hadn't created one when Opus found that image.
Lines that start with an '@' in the gallery macro say 'create an album and put any further images into that album'. Albums need a (unique) identifier (sensor and light in this case) so the Javascript knows what to do. You can suffix the identifier with '-nsfw' if you want to signpost that there is 'adult' content in the album. You can (optionally) follow the identifier with a comma and then a title for the album (as I did for the sensor chapter here). [You can swap back to just putting images into the top level of the gallery by just using '@' without an identifier if you want to intermix images and albums]
Portability
The Opus macros are tremendously powerful, and compared to pure HTML, actually quite convenient to use. But they do represent one major issue... portability.
The only thing in the world that understands these macros are the Opus PHP scripts. If you want to use your content somewhere else these macros won't get interpreted, they will just turn up as ugly chunks of text. Not ideal! Especially as Opus does not want to lock you into its world.
You can, of course, get pure HTML for a Webpage by using your browser's 'view source' feature. But this includes all of the gubbings of the page, not just the content itself which would make porting the content elsewhere a real headache.
However, you can easily convert an Opus rich-text, lazy HTML content file into pure HTML (without all of the page mechanics) using the 'text' service.
Given a URL such as:
https://antsmith.net/articles/opus-framework
You can retrieve a pure HTML version of just the work thusly:
If you have authornotes for the work as well you can also retrieve those as pure HTML:
https://antsmith.net/articles/text/authornotes/opus-framework
Just those two links will completely export all of your work as fully portable pure HTML.
To be fair, this service isn't perfect as yet. Titles do not get wrapped in heading tags and galleries do not get converted to simple <img> tags. But these issues will be addressed, all the more readily if someone asked me to! However, Opus has a firm commitment not to lock you and your content into its way of thinking!
Macro Round-Up
So that's about it for macros (except for tables, but again, see later). I guess it's a lot to get your head around, but ultimately there's only 13 of them to understand, try adopting them one by one until they become familiar.
The key concepts are:
Macros are either for styling & layout or the inclusion of additional assets
Styling can be influenced by using CSS classes after the macro type
Macros have plain-text definition lines
Styling & layout macros also have additional rich-text lines, which can themselves include macros
Asset macros have one or more file references
A file reference can target a whole bunch of files, or just one
Images can be requested at a fixed size, optionally with a given, crop or fit, aspect ratio
There are 2-levels to galleries (images and albums), with albums declared within the gallery macro
Don't forget, you can also use pure HTML for any other layouts you might want. And you can add your own styles (CSS) in the domain.css file.
Non-works
Not every page in an Opus Website is a published work. Each section has a 'top level' first page that (among other things) lists all the works in the section. This allows visitors to browse. This page is created automatically but you can influence what appears on it.
The lead image is stored in the images sub-directory with a base name the same as the URL_Part of the section (so that would be articles.jpg for this section of my site).
The metadata title and description are taken from a rich-text file stored in the authornotes sub-directory, again with a base name the same as the URL_Part of the section.
This file will also be displayed to the site visitor at the top of the page, but often that's hardly needed so display of it can be suppressed by prefixing the file with a hyphen (or minus sign).
You will see that this page also summarises the collections and tags that have been created in the section (see below).
It is also useful to be able to drop changing 'promotions' of content into this page since it is the first page of the section and visitors don't know what's what yet. You might elect to promote a major project or the latest work, for example, just to help orientate your visitors. This is especially useful on the top-level page of the 'home section', where people land when they just enter your site address (without requesting a specific page).
Promotions
Promotions are small panels of content that look similar to 'callouts', but rather than being a snippet of related content, they are 'tasters' of other materials. They are not added to work by a macro, they live in the promo sub-directory of the content directory and get displayed on the top-level page of the section.
There is a file called promo-list.txt that defines what promos get created and displayed. Each line defines one promo panel.
Existing site content can be promoted just by using the 'local URL' for the content. (A 'local URL' simply omits the HTTPS://<site address> part of a regular URL). So to create a promo panel for this page you would use 'articles/opus-framework'. Any page on the site can be promoted (e.g. articles/opus-framework/tag/technical).
You can also promote off-site content. E.g. if you have a related place in Cyberspace such as an online shop. In this case, create a rich-text file in the promo sub-directory.
The first four lines in a promo text file are:
Promo panel heading
Link address for panel heading (if any, can be blank)
Promo content title
Link address for the content title (if any, can be blank)
The rest of the file is normal Opus rich text.
Once you've made such a file you can include it in your list of promos by supplying the filename followed by your 'livery identifier'. The 'livery identifier' is just the URL_Part of the site section whose livery you want to use for the panel. That's a bit restrictive in terms of colouring things in, but is quite good enough for now!
You can also create a twitter timeline promo panel just by using the word 'twitter' in the promo-list file. You will have needed to supply your twitter handle in the site-config.php file.
Finally, you can also create a 'promo footer'. This isn't a panel like other promos, rather it sits underneath all of the other top-level page content, just above the common page footer, and so is the full page width. It's just an opportunity to add some more personal identity to this automated page. In my site home section, I use the promo-footer.txt file to present the Article 19 quotation. You don't need to list this in the promo-list.txt, it gets used if it happens to exist.
Contexts
In the 'normal' (canonical) context works are displayed in the main column of the page content area, with the lead assets displayed in the 'aside' column (on small screens the main column is the full-screen width and the aside column follows beneath it). There is a 'context bar' at the top and bottom of the content area that gives an idea of where in a site section the page sits and which provides navigation buttons. These buttons allow the visitor to move 'up' (from the display of a work to the list of all works), or else to cycle through the works.
Furthermore, works may be grouped either as:
editorial collections, or
tagged lists.
Collection
Collections are a curated list of works that have a cover image and a rich-text description. An online equivalent to a pamphlet, or an album etc...
They are defined by creating a text file in the collections sub-directory of the content directory. The file contains a title for the collection followed by a list of (existing) works that are in the collection. The list is terminated by '@@@' and then the rest of the file is Opus rich-text for the collection description. The text file will have a partner image file (in the same sub-directory) that completes the collection definition.
Tag
Tags are similar to collections except they do not have a rich-text description in the text file (they still do have a partner image file to represent the tag). They live in the tags sub-directory of the content directory.
There may be one special tag called nsfw.txt which identifies all works of an imprurient nature. If the title of the work is also imprurient then an alternative, prurient title can be supplied in this file. Add the prurient title after a comma following the work name in the list of works.
You can also add tag names or collections names into this list which allows those things to be declared as imprurient also.
There are also 2 automated tag groups:
001audio: contains all works that have mp3 assets
002video: contains all works that have mp4 assets
The automated tags are generated only if:
there are any works that have the associated asset type, and
there is a partner image file for the automated group - e.g. tags/001audio.jpg
Contextual Pages
When a work is displayed in the context of a group:
it has a longer URL form. Instead of simply [section]/[work] the URl has the pattern [section]/[work]/[context]/[group] - e.g. articles/opus-framework/tag/technical.
A list of all works within the same group is included in the main column
A list of other groups where the work can be found is added to the aside column
The 'up' button links to an automated page for the group
The left/right buttons are constrained so that they only cycle through the members of the group.
Each context (tags or collections) has its own automated aggregation page that:
has a URL of the form [section]/[context] - e.g. < ahref="articles/tags">articles/tags,
lists all of the groups of the context,
provides an 'up' button to return to the top-level list of all works.
Each group within a context has its own page that:
has a URL of the form [section]/[context]s/[group] - e.g. articles/tags,
lists all of the works in the group,
provides an 'up' button to return to the list of all groups of the context,
provides left/right buttons to cycle through each group of the context.
When creating the first group of a context you should also create the context assets - so that the automated context aggregation page gets its metatile and description. So that would collections/collections.txt and collections/collections.jpg (similarly for tags). Again, you can prefix the text files with a hyphen (or minus sign) to prevent this detail from being displayed on the page to the site visitor.
Overheads
Once set up, the overhead that Opus imposes on the content creator (over and above creating content) boils down to:
Uploading content or asset files
Managing lists of works for NSFW, Tags and Collections
Creating directories for assets and asset sets
Which doesn't seem too shabby given the zeroCMS objective.
But there are just a couple of other issues that need tending to, occasionally.
sitemaps
Sitemaps basically list every page a site has available. They are used by search engines to speed up the indexing of a Website; so improving the chance that a Website will be indexed by a search engine.
Because they are accessed by some of the busiest robots in the world they have to be available fast, really fast.
Once Opus has created the sitemap it stores a copy of it so that when a robot arrives it is instantly ready. Opus only creates the sitemap if it doesn't already exist.
This means that as you add content to the site, the stored sitemap starts to get out of date. Meaning that new content might not get indexed.
The solution is to periodically delete the sitemap so that Opus regenerates it.
The sitemap gets stored in the directory 'sitemaps' inside the public HTML folder of your Web server. There is one sitemap file for each section of the Website.
You should delete the sitemap files after every major site update, or else just from time to time.
Missing Content
As I said before, broken references are the scourge of the Internet. And they can happen on pages you're not even thinking about anymore because of some 'thoughtless' change that happens.
If Opus cannot find any content of the right type from a reference you provide, it will put a message into the Webpage. Here's a deliberately broken gallery so you can see what that looks like:
Gallery content MISSING or CORRUPT
Check macro ###gallery in your content file (gallery number 4).
- photography,photographic-skill,book-images/stoopid [MISSING no such current reference]
All such messages contain the uppercase word 'MISSING'. This means you can use the 'find' feature at the bottom of every Opus Webpage to search for that word (find is case-sensitive) and very quickly discover pages with broken references.
It's worth doing this whenever you do a major overhaul of your content.
Policies
Opus Websites have a section for storing policies where you can meet your legal obligations as a web publisher. You know stuff like privacy!
You should make sure the default policy is accurate for your implementation of Opus (editing it if not) when you set Opus up.
Also, if you add widgets to the site you'll need to understand how they impact the use of cookies and the tracking of visitors; again updating the site policy if needs be.
Deep Diving
The following is a round-up of information you might need whilst working with Opus but which isn't really needed to get started...
Inserts
domain.css allows you to add your own styling rules. It is the last style block to be used so takes order precedence. Opus does not use identifiers for styling so you should always be able to override framework style rules. The main content is always provided with the work base name as an id so all content will be reachable by CSS. Section livery styling can be applied using the class 'opus-URL_PART'
domain-csp allows you to extend the Content Security Protocol if you want to add additional external widgets. E.g. my site adds the bandcamp widget by providing:
$GLOBALS['TRUSTED']['frame-src'][] = 'https://bandcamp.com/';
$GLOBALS['TRUSTED']['child-src'][] = 'https://bandcamp.com/';
domain-head.php is inserted before the HTML head section closes and can be used to add additional head tags. E.g. to load external fonts:
<link href="https://fonts.googleapis.com/css2?family=Philosopher&display=swap" rel="preload" as="style" target="_blank"><i class="fa-solid fa-arrow-up-right-from-square"></i>
site-message.php is included after the menu bar and is primarily intended to add a consistent message to every page.
[section]/[work].php, if such exists, is included in the main content area of a work's page to provide complex features not directly supported by Opus. E.g. the PayPal 'buy it' panel on my Photographic Skill page.
Wiring
.htaccess in the public HTML web server root directory sends all non-file requests to the Opus master-script.php file and is key to the framework's correct operation; so be extremely careful when editing this! However, if you need to create redirects this is where you will do it...
[I may as well mention here that Opus has no Analytics built in. I was using Google but they have been evicted from the framework for bad net citizenship and I haven't had the motivation to create an alternative. Anyway analytics are unhealthy...]
Services
As well as delivering Webpages Opus provides 5 services that you might want to use if you are developing your own JavaScript. These operate either at the root or section level, depending on what you are trying to reach. E.g. for the text service:
<a href="text/framework/no-transcript">https://antsmith.net/text/framework/no-transcript
Services may require a single URL parameter (i.e. ?p=...)
The services are:
text: get the pure HTML rendition of an Opus rich-text file
image: retrieve a jpg image as an optimised webp inline asset of size 'p'
info: retrieve the title, description and metadata of a jpg image as a json string
like: perform a 'like' action (p=get,on,off) on a Webpage or image asset, returning json
find: find the text 'p' (case-sensitive) in works and authornotes in a section, returning json
Table Macro
Here's a simple table:
| Col 1 | Col 2 |
|---|---|
| Cell 1.1 | Cell 1.2 |
| Cell 2.1 | Cell 2.2 |
And the macro that creates it:
###table fit
|
Col 1|Col 2
Cell 1.1|Cell 1.2
Cell 2.1|Cell 2.2
###
Looks ugly, yeah? But compared with the HTML for the same table:
<div class="user-table fit">
<table id="myTable5">
<thead>
<tr id="tr-5-1">
<th>Col 1</th>
<th>Col 2</th>
</tr>
</thead>
<tbody>
<tr id="tr-5-2" class="tablerow even-row">
<td>Cell 1.1</td>
<td>Cell 1.2</td>
</tr>
<tr id="tr-5-3" class="tablerow odd-row">
<td>Cell 2.1</td>
<td>Cell 2.2</td>
</tr>
</tbody>
</table>
</div>
<div id="myModalTable5" class="tmodal">
<div id="tablediv5"></div>
</div>
Hopefully, that explains why I made the table Macro.
The macro works by providing one line of data for each row of the table. The line is split up into individual cells (for the table columns) by the '|' character. The first line of the table indicates what character(s) are used to separate the cell data - if your data happens to (or needs to) contain the '|' character then you can elect to use some other character(s) as the data separator. It just depends on what your data looks like.
Alternatively, you can forget about data separators altogether and instead tell Opus how many columns you want by supplying a number in the first line of the macro. Then each following row provides the data for one cell (not for one line). This makes your table macro longer, but you can then embed macros into your table. Here's an example of a rich table with embedded macros:
Chapter Highlights | Sample Image |
|---|---|
Light Highlights:
| |
Optics Highlights:
|
And here's the macro that creates such a table:
###table center fit
2
Chapter Highlights
Sample Image
###ul
Light Highlights:
The nature of light
Modelling light...
###
###img fit
photography,photographic-skill,book-images/light/l05-01
Sample page from LIght Chapter
80
###
###ul
Optics Highlights:
Depth of Field
Perspective...
###
###img fit
photography,photographic-skill,book-images/optics/o00-02
Sample image from Optics Chapter
80
###
###
Now I admit it's annoying having to think about whether you are creating a simple table or a rich table. It would be more convenient to just allow macros to be embedded, but macros interrupt the normal flow of text. This screws-up Opus's understanding of where one row finishes and another row starts. Annoying as this might be, I still think it's better than having to type in the pure HTML that would create such a table, which in fact looks like this:
<div class="user-table center fit">
<table id="myTable6">
<thead>
<tr id="tr-6-1">
<th><p>Chapter Highlights</p></th>
<th><p>Sample Image</p></th>
</tr>
</thead>
<tbody>
<tr id="tr-6-2" class="tablerow even-row">
<td>
<div class="user-ul">
<p>Light Highlights:</p>
<ul>
<li><p>The nature of light</p></li>
<li><p>Modelling light...</p></li>
</ul>
</div>
</td>
<td>
<div class="safe-container user-img fit">
<img
loading="lazy"
id="myImgimg7"
class="myImg"
width="80"
height="100"
alt="Sample page from LIght Chapter"
title="Sample page from LIght Chapter"
src="photography/image/images/photographic-skill/book-images/light/l05-01?p=80&v=2.023.3"
data-ngsrc="photography/image/images/photographic-skill/book-images/light/l05-01?p=2048&v=2.023.3"
data-nanogallery2-lightbox = '{
"viewerToolbar": {
"display": false,
"position": "top",
"standard": "minimizeButton,label",
"minimized": "minimizeButton" },
"locationHash": true,
"viewerTools": {
"topLeft": "pageCounter, playPauseButton",
"topRight": "custom2, fullscreenButton, closeButton" },
"fnImgDisplayed": "ng2_display_incidentals",
"fnImgToolbarCustClick": "ng2_cc_incidentals",
"icons": {
"viewerCustomTool1" : "<i class='fas fa-info-circle'></i>",
"viewerCustomTool2" : "<i id='myHeartincidentals' class='far fa-heart'></i>" }}'
srcset="photography/image/images/photographic-skill/book-images/light/l05-01?p=200&v=2.023.3 200w,
photography/image/images/photographic-skill/book-images/light/l05-01?p=320&v=2.023.3 320w,
photography/image/images/photographic-skill/book-images/light/l05-01?p=512&v=2.023.3 512w,
photography/image/images/photographic-skill/book-images/light/l05-01?p=1024&v=2.023.3 1024w,
photography/image/images/photographic-skill/book-images/light/l05-01?p=1536&v=2.023.3 1536w,
photography/image/images/photographic-skill/book-images/light/l05-01?p=2048&v=2.023.3 2048w "
sizes="(min-width: 2048px) 2048px, 100vw" >
</div>
</td>
</tr>
<tr id="tr-6-3" class="tablerow odd-row">
<td>
<div class="user-ul">
<p>Optics Highlights:</p>
<ul>
<li><p>Depth of Field</p></li>
<li><p>Perspective...</p></li>
</ul>
</div>
</td>
<td>
<div class="safe-container user-img fit">
<img
loading="lazy"
id="myImgimg7"
class="myImg"
width="80"
height="100"
alt="Sample image from Optics Chapter"
title="Sample image from Optics Chapter"
src="photography/image/images/photographic-skill/book-images/optics/o00-02?p=80&v=2.023.3"
data-ngsrc="photography/image/images/photographic-skill/book-images/optics/o00-02?p=2048&v=2.023.3"
data-nanogallery2-lightbox = '{
"viewerToolbar": {
"display": false,
"position": "top",
"standard": "minimizeButton,label",
"minimized": "minimizeButton" },
"locationHash": true,
"viewerTools": {
"topLeft": "pageCounter, playPauseButton",
"topRight": "custom2, fullscreenButton, closeButton" },
"fnImgDisplayed": "ng2_display_incidentals",
"fnImgToolbarCustClick": "ng2_cc_incidentals",
"icons": {
"viewerCustomTool1" : "<i class='fas fa-info-circle'></i>",
"viewerCustomTool2" : "<i id='myHeartincidentals' class='far fa-heart'></i>" }}'
srcset="photography/image/images/photographic-skill/book-images/optics/o00-02?p=200&v=2.023.3 200w,
photography/image/images/photographic-skill/book-images/optics/o00-02?p=320&v=2.023.3 320w,
photography/image/images/photographic-skill/book-images/optics/o00-02?p=512&v=2.023.3 512w,
photography/image/images/photographic-skill/book-images/optics/o00-02?p=1024&v=2.023.3 1024w,
photography/image/images/photographic-skill/book-images/optics/o00-02?p=1536&v=2.023.3 1536w,
photography/image/images/photographic-skill/book-images/optics/o00-02?p=2048&v=2.023.3 2048w "
sizes="(min-width: 2048px) 2048px, 100vw" >
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div id="myModalTable6" class="tmodal">
<div id="tablediv6"></div>
</div>
The Next Generation...
This is just a scratchpad of what the next round of updates might cover:
automate sitemap updates
check and self-healing scripts
flexible templating (this will be the big win)
more use of promos
prevent accordions in accordions
Direct accordions (without needing separate files)
Automatic accordions for galleries
User transcript display for audio playlists
Text service export vs. transcript processing
Likes reporting and exporting
Analytics!
Styling tweaks (esp. ta target review)
Allow big lead assets in the content area
Simpler redirects!
And honestly, there will be another 100% rewrite... I just know it. I can't help myself. Having shifted from a pure scripting paradigm to a neophyte OOPs approach the code is pretty handsome now, and I'm loving how it deploys creative metaphors - how it is written with narrative, character, and compositional flow... I'm also loving how the code base has shrunk from over 20,000 lines to around 7,500 lines. But I think the code suffers from its evolutionary path - I'm sure it has at least one appendix waiting to burst! I can probably reduce the code base further by starting again from scratch, now that all the concepts and implementation details are so well-known. I know the code is capable of expressing great beauty. But it would be lunacy to do that now, after this major trawl through everything. It will have to wait. It must wait.
Ant Smith, 2023
You can:
Download this framework for free here, OR
You could take a moment to value the efforts and hit the DONATE button in the footer of the page first.