Left Arrow

Notes

Floating Image

Placeholder text wrapped around a image wireframe

Floating Image

Intended Audience: Front-end developers. Anyone learning CSS.

Tended

Status: seed

The default medium used to communicate on a web page is text. To better communicate, it's common for include an image or video with the text. Designing how these elements are positioned can further improve communication. For example. This article about a pilot's famous flight between England and Australia. See how the position of text and image reinforces this story.

An article about a flight. The text wrapping around a silhouette of England of the left and Australia on the right.

Text wrapping is a useful tool when positioning elements. Text (and other inline elements) can wrap around an element using the float property. This can be refined by also using shape-outside with a value of:

  • circle(),
  • ellipse(),
  • inset() for rectangular shapes,
  • polygon() for any shape with 3 or more vertices and
  • url() to generate the shape from an image.

float

Below is an example of a div with float. float is limited to:

  • positioning the element to the top-left or top-right corner of the following sybling inline element and
  • the shape the text flows around is rectangular (text wraps around the element's box - made up of content, padding, border and margin).

localhost:3000

div {
    width: 40%;
    aspect-ratio: 1/1;

    float: right;
    border: 1px solid var(--black);
}

p {
    text-align: justify;
}

p + p {
    margin-top: 24px;
}

shape-outside

The shape-outside property creates an invisible shape within an element with float. Sybling inline elements will wrap around this shape instead of the element's box. Any part of the shape outside the box is ignored.

/index.css

div {
    float: right;
                /* circle(<radius> at <x>, <y>) */
    shape-outside: circle(50% at 50% 50%);
}
Drag me

localhost:3000

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Working out the values required for shape-outside using a code editor can be tedious. An easier why is to use Firefox's dev tools. It provides an interface to generate the values. Say you wanted to use a polygon shape. In your editor, set values to a simple 3-side polygon, shape-outside: polygon(0 0, 100% 0, 100% 100%);. Then, open the dev tools, inspect the element and click the icon next to polygon. Now you can:

  • drag a point to different position,
  • double-click the line to add a point and
  • shift-double-click a point to remove it.
Screenshot of Firefox browser dev tools

shape-margin

shape-margin adds a margin to the shape. Like the shape, any part of the margin outside the element's box is ignored.

/index.css

div {
    float: right;
    shape-outside: circle(50% at 50% 50%);
    shape-margin: 16px;
}
Drag me

localhost:3000

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

shape-image-threshold

An alternative to manually defining a shape is shape-image-threshold. It creates a shape based on the transparency of pixels. It takes a threshold value. The shape is defined by the pixels whose alpha value is greater than the threshold. A value of:

  • 0.0 = full transparent
  • 1.0 = fully opaque (not able to be seen through)

Below, the threshold is set to 0.1. This means the shape will be defined by all pixels that are more than 10% opaque.

localhost:3000

img {
    width: 40%;

    float: right;
    shape-outside: url('https://garden.bradwoods.io/images/circle.svg');
    shape-image-threshold: 0.1;

    border: 1px solid var(--black);
}

Gap

If an image asset doesn't contain any empty space and a gap is required, shape-margin can't be used. This is because any part of the margin that goes outside the image's element box is ignored. A work-around is to apply float and shape properties to a parent element. Then, add padding equal to the shape-margin value. This technique can also move the shape away from the top.

localhost:3000

:root {
    --gap: 16px;
}

div {
    width: 40%;
    padding: var(--gap) 0 var(--gap) var(--gap);

    float: right;
    shape-outside: url('https://garden.bradwoods.io/images/circle.svg');
    shape-image-threshold: 0.1;
    shape-margin: var(--gap);

    border: 1px solid var(--black);
}

img {
    width: 100%;
}

shape-image-threshold is a great fit when working with images with a gradient. Giving control of how much text can overlap the image. When doing this, care must be taken to ensure text has enough contrast to have a AA rating.

2 passages from a historical biblical text with decorative drop caps

Use Case - Drop Cap

A drop cap (dropped capital) is a large, decorative initial letter. Usually located at the beginning of a section or chapter of a book. Their use in religious and scholarly texts dates back almost 2000 years. Making them associated with, and communicate, an old, traditional feeling. They stand out by using contrast. Being larger and more decorated than the letters around them. They are used for:

  • adding depth to what is being communicated in the text by incorporating an image,
  • as a visual aid by providing anchor points. Helping users find their place when reading large amounts of text and
  • to mark a new section, new idea or important passage.

Floating image techniques can be used to create a drop cap.

localhost:3000

<svg viewBox="0 0 100 100">
    <text x="24" y="82">L</text>
    <!-- 1 and 98 instead of 0 and 100 to prevent stroke cut off -->
    <rect x="1" y="1" width="98" height="98" />
</svg>
<p>
    orem ipsum dolor sit amet, consectetur *adipiscing elit, sed do eiusmod tem*por incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>

If the intention of a drop cap is to draw the user's attention, we can use the browser's capabilities to do more than just display a static image. Known as going beyond skeuomorphic. Humans attention is drawn to movement. Some believe this behaviour comes from our ancient ancestors who needed to be aware of moving things around them in case of predators. We can use this behaviour to make a drop cap more effective by adding an animation.

localhost:3000

<svg viewBox="0 0 100 100">
    <text x="24" y="82">L</text>
    <rect x="4" y="4" width="92" height="92" />
    <circle r="2">
        <animateMotion
            dur="40s"
            path="M 4 4 H 96 V 96 H 4 L 4 4"
            repeatCount="indefinite"
        />
    </circle>
</svg>
<p>
    orem ipsum dolor sit amet, consectetur *adipiscing elit, sed do eiusmod tem*por incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>

You could argue a constant animation is too distracting to the user. Once their attention is drawn to a section, the animation has fulfilled its purpose. It's no longer required. Instead of a constant animation, we could use an entrance animation. A short animation that plays when the user 1st sees the element. This approach requires the Intersection Observer.

localhost:3000

svg {
    width: 68px;
    margin-right: 8px;

    float: left;
}

rect {
    fill: none;
    stroke: var(--black);
}

text {
    font-size: 96px;
    fill: var(--black);

    transform: translateY(100%);
    transition: transform 3s ease-out;
}

.show {
    transform: translateY(0%);
}

/* For accessibility, remove animation for users who enabled reduced motion preference setting */
@media screen and (prefers-reduced-motion: reduce) {
    text {
        transition-duration: 0s;
    }
}

Where to Next?