Tag: Generate

Generate a Pull Request of Static Content With a Simple HTML Form

Jamstack has been in the website world for years. Static Site Generators (SSGs) — which often have content that lives right within a GitHub repo itself — are a big part of that story. That opens up the idea of having contributors that can open pull requests to add, change, or edit content. Very useful!

Examples of this are like:

Why built with a static site approach?

When we need to build content-based sites like this, it’s common to think about what database to use. Keeping content in a database is a time-honored good idea. But it’s not the only approach! SSGs can be a great alternative because…

  • They are cheap and easy to deploy. SSGs are usually free, making them great for an MVP or a proof of concept.
  • They have great security. There is nothing to hack through the browser, as all the site contains is often just static files.
  • You’re ready to scale. The host you’re already on can handle it.

There is another advantage for us when it comes to a content site. The content of the site itself can be written in static files right in the repo. That means that adding and updating content can happen right from pull requests on GitHub, for example. Even for the non-technically inclined, it opens the door to things like Netlify CMS and the concept of open authoring, allowing for community contributions.

But let’s go the super lo-fi route and embrace the idea of pull requests for content, using nothing more than basic HTML.

The challenge

How people contribute adding or updating a resource isn’t always perfectly straightforward. People need to understand how to fork your repository, how to and where to add their content, content formatting standards, required fields, and all sorts of stuff. They might even need to “spin up” the site themselves locally to ensure the content looks right.

People who seriously want to help our site sometimes will back off because the process of contributing is a technological hurdle and learning curve — which is sad.

You know what anybody can do? Use a <form>

Just like a normal website, the easy way for people to submit a content is to fill out a form and submit it with the content they want.

What if we can make a way for users to contribute content to our sites by way of nothing more than an HTML <form> designed to take exactly the content we need? But instead of the form posting to a database, it goes the route of a pull request against our static site generator? There is a trick!

The trick: Create a GitHub pull request with query parameters

Here’s a little known trick: We can pre-fill a pull request against our repository by adding query parameter to a special GitHub URL. This comes right from the GitHub docs themselves.

Let’s reverse engineer this.

If we know we can pre-fill a link, then we need to generate the link. We’re trying to make this easy remember. To generate this dynamic data-filled link, we’ll use a touch of JavaScript.

So now, how do we generate this link after the user submits the form?

Demo time!

Let’s take the Serverless site from CSS-Tricks as an example. Currently, the only way to add a new resource is by forking the repo on GitHub and adding a new Markdown file. But let’s see how we can do it with a form instead of jumping through those hoops.

The Serverless site itself has many categories (e.g. for forms) we can contribute to. For the sake of simplicity, let’s focus on the “Resources” category. People can add articles about things related to Serverless or Jamstack from there.

The Resources page of the CSS-Tricks Serverless site. The site is primarily purple in varying shades with accents of orange. The page shows a couple of serverless resources in the main area and a list of categories in the right sidebar.

All of the resource files are in this folder in the repo.

Showing the main page of the CSS-Tricks Serverless repo in GitHub, displaying all the files.

Just picking a random file from there to explore the structure…

--- title: "How to deploy a custom domain with the Amplify Console" url: "https://read.acloud.guru/how-to-deploy-a-custom-domain-with-the-amplify-console-a884b6a3c0fc" author: "Nader Dabit" tags: ["hosting", "amplify"] ---  In this tutorial, we’ll learn how to add a custom domain to an Amplify Console deployment in just a couple of minutes.

Looking over that content, our form must have these columns:

  • Title
  • URL
  • Author
  • Tags
  • Snippet or description of the link.

So let’s build an HTML form for all those fields:

<div class="columns container my-2">   <div class="column is-half is-offset-one-quarter">   <h1 class="title">Contribute to Serverless Resources</h1>    <div class="field">     <label class="label" for="title">Title</label>     <div class="control">       <input id="title" name="title" class="input" type="text">     </div>   </div>      <div class="field">     <label class="label" for="url">URL</label>     <div class="control">       <input id="url" name="url" class="input" type="url">     </div>   </div>        <div class="field">     <label class="label" for="author">Author</label>     <div class="control">       <input id="author" class="input" type="text" name="author">     </div>   </div>      <div class="field">     <label class="label" for="tags">Tags (comma separated)</label>     <div class="control">       <input id="tags" class="input" type="text" name="tags">     </div>   </div>        <div class="field">     <label class="label" for="description">Description</label>     <div class="control">       <textarea id="description" class="textarea" name="description"></textarea>     </div>   </div>       <!-- Prepare the JavaScript function for later -->   <div class="control">     <button onclick="validateSubmission();" class="button is-link is-fullwidth">Submit</button>   </div>        </div> </div>

I’m using Bulma for styling, so the class names in use here are from that.

Now we write a JavaScript function that transforms a user’s input into a friendly URL that we can combine as GitHub query parameters on our pull request. Here is the step by step:

  • Get the user’s input about the content they want to add
  • Generate a string from all that content
  • Encode the string to format it in a way that humans can read
  • Attach the encoded string to a complete URL pointing to GitHub’s page for new pull requests

Here is the Pen:

After pressing the Submit button, the user is taken right to GitHub with an open pull request for this new file in the right location.

GitHub pull request screen showing a new file with content.

Quick caveat: Users still need a GitHub account to contribute. But this is still much easier than having to know how to fork a repo and create a pull request from that fork.

Other benefits of this approach

Well, for one, this is a form that lives on our site. We can style it however we want. That sort of control is always nice to have.

Secondly, since we’ve already written the JavaScript, we can use the same basic idea to talk with other services or APIs in order to process the input first. For example, if we need information from a website (like the title, meta description, or favicon) we can fetch this information just by providing the URL.

Taking things further

Let’s have a play with that second point above. We could simply pre-fill our form by fetching information from the URL provided for the user rather than having them have to enter it by hand.

With that in mind, let’s now only ask the user for two inputs (rather than four) — just the URL and tags.

How does this work? We can fetch meta information from a website with JavaScript just by having the URL. There are many APIs that fetch information from a website, but you might the one that I built for this project. Try hitting any URL like this:

https://metadata-api.vercel.app/api?url=https://css-tricks.com

The demo above uses that as an API to pre-fill data based on the URL the user provides. Easier for the user!

Wrapping up

You could think of this as a very minimal CMS for any kind of Static Site Generator. All you need to do is customize the form and update the pre-filled query parameters to match the data formats you need.

How will you use this sort of thing? The four sites we saw at the very beginning are good examples. But there are so many other times where might need to do something with a user submission, and this might be a low-lift way to do it.


The post Generate a Pull Request of Static Content With a Simple HTML Form appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , , , ,

Smarter Ways to Generate a Deep Nested HTML Structure

Let’s say we want to have the following HTML structure:

<div class='boo'>   <div class='boo'>     <div class='boo'>       <div class='boo'>         <div class='boo'></div>       </div>     </div>   </div> </div>

That’s real a pain to write manually. And the reason why this post was born was being horrified on seeing it generated with Haml like this:

.boo   .boo     .boo       .boo         .boo

There were actually about twenty levels of nesting in the code I saw, but maybe some people are reading thing on a mobile phone, so let’s not fill the entire viewport with boos, even if Halloween is near.

As you can probably tell, manually writing out every level is far from ideal, especially when the HTML is generated by a preprocessor (or from JavaScript, or even a back-end language like PHP). I’m personally not a fan of deep nesting and I don’t use it much myself, but if you’re going for it anyway, then I think it’s worth doing in a manner that scales well and is easily maintainable.

So let’s first take a look at some better solutions for this base case and variations on it and then see some fun stuff done with this kind of deep nesting!

The base solution

What we need here is a recursive approach. For example, with Haml, the following bit of code does the trick:

- def nest(cls, n); -  return '' unless n > 0; -  "<div class='#{cls}'>#{nest(cls, n - 1)}</div>"; end  = nest('👻', 5)

There’s an emoji class in there because we can and because this is just a fun little example. I definitely wouldn’t use emoji classes on an actual website, but in other situations, I like to have a bit of fun with the code I write.

We can also generate the HTML with Pug:

mixin nest(cls, n)   div(class=cls)     if --n       +nest(cls, n)  +nest('👻', 5)

Then there’s also the JavaScript option:

function nest(_parent, cls, n) {   let _el = document.createElement('div'); 	   if(--n) nest(_el, cls, n);    _el.classList.add(cls);   _parent.appendChild(_el) };  nest(document.body, '👻', 5)

With PHP, we can use something like this:

<?php function nest($  cls, $  n) {   echo "<div class='$  cls'>";   if(--$  n > 0) nest($  cls, $  n);   echo "</div>"; }  nest('👻', 5); ?>

Note that the main difference between what each of these produce is related to formatting and white space. This means that targeting the innermost “boo” with .👻:empty is going to work for the Haml, JavaScript and PHP-generated HTML, but will fail for the Pug-generated one.

Adding level indicators

Let’s say we want each of our boos to have a level indicator as a custom property --i, which could then be used to give each of them a different background, for example.

You may be thinking that, if all we want is to change the hue, then we can do that with filter: hue-rotate() and do without level indicators. However, hue-rotate() doesn’t only affect the hue, but also the saturation and lightness. It also doesn’t provide the same level of control as using our own custom functions that depend on a level indicator, --i.

For example, this is something I used in a recent project in order to make background components smoothly change from level to level (the $ c values are polynomial coefficients):

--sq: calc(var(--i)*var(--i)); /* square */ --cb: calc(var(--sq)*var(--i)); /* cube */ --hue: calc(#{$  ch0} + #{$  ch1}*var(--i) + #{$  ch2}*var(--sq) + #{$  ch3}*var(--cb)); --sat: calc((#{$  cs0} + #{$  cs1}*var(--i) + #{$  cs2}*var(--sq) + #{$  cs3}*var(--cb))*1%); --lum: calc((#{$  cl0} + #{$  cl1}*var(--i) + #{$  cl2}*var(--sq) + #{$  cl3}*var(--cb))*1%);  background: hsl(var(--hue), var(--sat), var(--lum));

Tweaking the Pug to add level indicators looks as follows:

mixin nest(cls, n, i = 0)   div(class=cls style=`--i: $  {i}`)     if ++i < n       +nest(cls, n, i)  +nest('👻', 5)

The Haml version is not too different either:

- def nest(cls, n, i = 0); -   return '' unless i < n; -   "<div class='#{cls}' style='--i: #{i}'>#{nest(cls, n, i + 1)}</div>"; end  = nest('👻', 5)

With JavaScript, we have:

function nest(_parent, cls, n, i = 0) {   let _el = document.createElement('div');    _el.style.setProperty('--i', i); 	   if(++i < n) nest(_el, cls, n, i);    _el.classList.add(cls);   _parent.appendChild(_el) };  nest(document.body, '👻', 5)

And with PHP, the code looks like this:

<?php function nest($  cls, $  n, $  i = 0) {   echo "<div class='$  cls' style='--i: $  i'>";   if(++$  i < $  n) nest($  cls, $  n, $  i);   echo "</div>"; }  nest('👻', 5); ?>

A more tree-like structure

Let’s say we want each of our boos to have two boo children, for a structure that looks like this:

.boo   .boo     .boo       .boo       .boo     .boo       .boo       .boo   .boo     .boo       .boo       .boo     .boo       .boo       .boo

Fortunately, we don’t have to change our base Pug mixin much to get this (demo):

mixin nest(cls, n)   div(class=cls)     if --n       +nest(cls, n)       +nest(cls, n)  +nest('👻', 5)

The same goes for the Haml version:

- def nest(cls, n); -   return '' unless n > 0; -   "<div class='#{cls}'>#{nest(cls, n - 1)}#{nest(cls, n - 1)}</div>"; end  = nest('👻', 5)

The JavaScript version requires a bit more effort, but not too much:

function nest(_parent, cls, n) {   let _el = document.createElement('div');      if(n > 1) {     nest(_el, cls, n);     nest(_el, cls, n)   }    _el.classList.add(cls);   _parent.appendChild(_el) };  nest(document.body, '👻', 5)

With PHP, we only need to call the nest() function once more in the if block:

<?php function nest($  cls, $  n) {   echo "<div class='$  cls'>";   if(--$  n > 0) {     nest($  cls, $  n);     nest($  cls, $  n);   }   echo "</div>"; }  nest('👻', 5); ?>

Styling the top level element differently

We could of course add a special .top (or .root or anything similar) class only for the top level, but I prefer leaving this to the CSS:

:not(.👻) > .👻 {   /* Top-level styles*/ }

Watch out!

Some properties, such as transform, filter, clip-path, mask or opacity don’t only affect an element, but also also all of its descendants. Sometimes this is the desired effect and precisely the reason why nesting these elements is preferred to them being siblings.

However, other times it may not be what we want, and while it is possible to reverse the effects of transform and sometimes even filter, there’s nothing we can do about the others. We cannot, for example, set opacity: 1.25 on an element to compensate for its parent having opacity: .8.

Examples!

First off, we have this pure CSS dot loader I recently made for a CodePen challenge:

Here, the effects of the scaling transforms and of the animated rotations add up on the inner elements, as do the opacities.

Next up is this yin and yang dance, which uses the tree-like structure:

For every item, except the outermost one (:not(.☯️) > .☯️), the diameter is equal to half of that of its parent. For the innermost items (.☯️:empty, which I guess we can call the tree leaves), the background has two extra radial-gradient() layers. And just like the first demo, the effects of the animated rotations add up on the inner elements.

Another example would be these spinning candy tentacles:

Each of the concentric rings represents a level of nesting and combines the effects of the animated rotations from all of its ancestors with its own.

Finally, we have this triangular openings demo (note that it’s using individual transform properties like rotate and scale so the Experimental Web Platform features flag needs to be enabled in chrome://flags in order to see it working in Chromium browsers):

Triangular openings (live demo).

This uses a slightly modified version of the basic nesting mixin in order to also set a color on each level:

- let c = ['#b05574', '#f87e7b', '#fab87f', '#dcd1b4', '#5e9fa3']; - let n = c.length;  mixin nest(cls, n)   div(class=cls style=`color: $  {c[--n]}`)     if n       +nest(cls, n)  body(style=`background: $  {c[0]}`)   +nest('🔺', n)

What gets animated here are the individual transform properties scale and rotate. This is done so that we can set different timing functions for them.


The post Smarter Ways to Generate a Deep Nested HTML Structure appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , , ,
[Top]

Using Artificial Intelligence to Generate Alt Text on Images

Web developers and content editors alike often forget or ignore one of the most important parts of making a website accessible and SEO performant: image alt​ text. You know, that seemingly small image attribute that describes an image:

​​​<img src="/cute/sloth/image.jpg" alt="A brown baby sloth staring straight into the camera with a tongue sticking out." >

A brown baby sloth staring straight into the camera with a tongue sticking out.
📷 Credit: Huffington Post

If you regularly publish content on the web, then you know it can be tedious trying to come up with descriptive text. Sure, 5-10 images is doable. But what if we are talking about hundreds or thousands of images? Do you have the resources for that?

Let’s look at some possibilities for automatically generating alt text for images with the use of computer vision and image recognition services from the likes Google, IBM, and Microsoft. They have the resources!

Reminder: What is alt text good for?

Often overlooked during web development and content entry, the alt​ attribute is a small bit of HTML code that describes an image that appears on a page. It’s so inconspicuous that it may not appear to have any impact on the average user, but it has very important uses indeed:

  • ​​Web Accessibility for Screen Readers: Imagine a page with lots of images and not a single one contains alt​ text. A user surfing in using a screen reader would only hear the word “image” blurted out and that’s not very helpful. Great, there’s an image, but what is it? Including alt​ enables screen readers to help the visually impaired “see” what’s there and have a better understanding of the content of the page. They say a picture is worth a thousand words — that’s a thousand words of context a user could be missing.
  • Display text if an image does not load: The World Wide Web seems infallible and, like New York City, that it never sleeps, but flaky and faulty connections are a real thing and, if that happens, well, images tend not to load properly and “break.” Alt text is a safeguard in that it displays on the page in place of where the “broken” image is, providing users with content as a fallback.
  • ​​SEO performance: Alt text on images contributes to SEO performance as well. Though it doesn’t exactly help a site or page skyrocket to the top of the search results, it is one factor to keep in mind for SEO performance.

Knowing how important these things are, hopefully you’ll be able to include proper alt​ text during development and content entry. But are your archives in good shape? Trying to come up with a detailed description for a large backlog of images can be a daunting task, especially if you’re working on tight deadlines or have to squeeze it in between other projects.

What if there was a way to apply alt​ text as an image is uploaded? And! What if there was a way to check the page for missing alt​ tags and automagically fill them in for us?

There are available solutions!

Computer vision (or image recognition) has actually been offered for quite some time now. Companies like Google, IBM and Microsoft have their own APIs publicly available so that developers can tap into those capabilities and use them to identify images as well as the content in them.

There are developers who have already utilized these services and created their own plugins to generate alt​ text. Take Sarah Drasner’s generator, for example, which demonstrates how Azure’s Computer Vision API can be used to create alt​ text for any image via upload or URL. Pretty awesome!

​​See the Pen
​​Dynamically Generated Alt Text with Azure's Computer Vision API
by Sarah Drasner (@sdras)
​​on CodePen.
​​

There’s also Automatic Alternative Text by Jacob Peattie, which is a WordPress plugin that uses the same Computer Vision API. It’s basically an addition to the workflow that allows the user to upload an image and generated alt​ text automatically.

​​Tools like these generally help speed-up the process of content management, editing and maintenance. Even the effort of thinking of a descriptive text has been minimized and passed to the machine!

Getting Your Hands Dirty With AI

I have managed to have played around with a few AI services and am confident in saying that Microsoft Azure’s Computer Vision produces the best results. The services offered by Google and IBM certainly have their perks and can still identify images and proper results, but Microsoft’s is so good and so accurate that it’s not worth settling for something else, at least in my opinion.

Creating your own image recognition plugin is pretty straightforward. First, head down to Microsoft Azure Computer Vision. You’ll need to login or create an account in order to grab an API key for the plugin.

Once you’re on the dashboard, search and select Computer Vision and fill in the necessary details.

Starting out

Wait for the platform to finish spinning up an instance of your computer vision. The API keys for development will be available once it’s done.

​​Keys: Also known as the Subscription Key in the official documentation

Let the interesting and tricky parts begin! I will be using vanilla JavaScript for the sake of demonstration. For other languages, you can check out the documentation. Below is a straight-up copy and paste of the code and you can use to replace the placeholders.

​​var request = new XMLHttpRequest(); request.open('POST', 'https://[LOCATION]/vision/v1.0/describe?maxCandidates=1&language=en', true); request.setRequestHeader('Content-Type', 'application/json'); request.setRequestHeader('Ocp-Apim-Subscription-Key', '[SUBSCRIPTION_KEY]'); request.send(JSON.stringify({ "url": "[IMAGE_URL]" })); request.onload = function () {     var resp = request.responseText;     if (request.status >= 200 && request.status < 400) {         // Success!         console.log('Success!');     } else {         // We reached our target server, but it returned an error         console.error('Error!');     }      console.log(JSON.parse(resp)); };  request.onerror = function (e) {     console.log(e); };

Alright, let’s run through some key terminology of the AI service.

  • Location: This is the subscription location of the service that was selected prior to getting the subscription keys. If you can’t remember the location for some reason, you can go to the Overview screen and find it under Endpoint.
  • ​​

Overview > Endpoint : To get the location value
  • ​​Subscription Key: This is the key that unlocks the service for our plugin use and can be obtained under Keys. There’s two of them, but it doesn’t really matter which one is used.
  • ​​Image URL: This is the path for the image that’s getting the alt​ text. Take note that the images that are sent to the API must meet specific requirements:
    • File type must be JPEG, PNG, GIF, BMP
    • ​File size must be less than 4MB
    • ​​Dimensions should be greater than 50px by 50px

Easy peasy

​​Thanks to big companies opening their services and API to developers, it’s now relatively easy for anyone to utilize computer vision. As a simple demonstration, I uploaded the image below to Microsoft Azure’s Computer Vision API.

Possible alt​ text: a hand holding a cellphone

​​The service returned the following details:

​​{     "description": {         "tags": [             "person",             "holding",             "cellphone",             "phone",             "hand",             "screen",             "looking",             "camera",             "small",             "held",             "someone",             "man",             "using",             "orange",             "display",             "blue"         ],         "captions": [             {                 "text": "a hand holding a cellphone",                 "confidence": 0.9583763512737793             }         ]     },     "requestId": "31084ce4-94fe-4776-bb31-448d9b83c730",     "metadata": {         "width": 920,         "height": 613,         "format": "Jpeg"     } }

​​From there, you could pick out the alt​ text that could be potentially used for an image. How you build upon this capability is your business:

  • ​​You could create a CMS plugin and add it to the content workflow, where the alt​ text is generated when an image is uploaded and saved in the CMS.
  • ​​You could write a JavaScript plugin that adds alt​ text on-the-fly, after an image has been loaded with notably missing alt​ text.
  • ​​You could author a browser extension that adds alt​ text to images on any website when it finds images with it missing.
  • ​​You could write code that scours your existing database or repo of content for any missing alt​ text and updates them or opens pull requests for suggested changes.

​​Take note that these services are not 100% accurate. They do sometimes return a low confidence rating and a description that is not at all aligned with the subject matter. But, these platforms are constantly learning and improving. After all, Rome wasn’t built in a day.

The post Using Artificial Intelligence to Generate Alt Text on Images appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]