Struct is non-literal type

You should be able to make black and white into static constexpr functions--i.e. this is an example of the "named-constructor idiom."

struct rgb_color {
    constexpr rgb_color(std::uint8_t nr, std::uint8_t ng, std::uint8_t nb) :
    r(nr), g(ng), b(nb) { }

    std::uint8_t r; // red
    std::uint8_t g; // green
    std::uint8_t b; // blue

    constexpr static rgb_color black() { return rgb_color(0, 0, 0); }
    constexpr static rgb_color white() { return rgb_color(255, 255, 255); }
};

Why not this?

struct rgb_color {
    constexpr rgb_color(std::uint8_t nr, std::uint8_t ng, std::uint8_t nb) :
        r(nr), g(ng), b(nb) { }

    std::uint8_t r; // red
    std::uint8_t g; // green
    std::uint8_t b; // blue

    static const rgb_color black;
    static const rgb_color white;
};

const rgb_color rgb_color::black {0, 0, 0};
const rgb_color rgb_color::white {255, 255, 255};

This doesn't work, because you are instantiating a type that is not fully declared yet (you have not reached the closing brace and semicolon yet, so rgb_color is still an incomplete type).

You can work around this by declaring your constants out of the class, maybe in their own namespace:

namespace rgb_color_constants {
    constexpr static rgb_color black = rgb_color(0, 0, 0);
    constexpr static rgb_color white = rgb_color(255, 255, 255);
}