Individual bit access in C
This code is not portable.  The order of bit-fields within a word is completely compiler-dependent, so the test that appears to work on one platform may give completely different results on another.
You have avoided a common trap of using signed 1-bit fields (which can hold values 0 and -1) - these unsigned ones are much better.
I don't think there's any need for the bits member to be named - I would use an anonymous member there.
The numbering of bits is unconventional - most programmers would expect b0 to be the least-significant bit, and b7 the most significant (corresponding to 2⁰ and 2⁷ value of those bits in integers).
The test would be better if it were self-checking - return with non-zero status if any of the expectations are not met.  For example:
int main(void)
{
    int failures = 0;

    {
        /* First test */
        union bit_array test = { {1, 1, 1, 1, 1, 1, 1, 1} };
        if (test.value != 0xFF) {
            fprintf(stderr, &quot;Test 1 expected 0xff, got %#04hhx\n&quot;, test.value);
            ++failures;
        }
    }

    {
        /* Second test */
        union bit_array test = { {1, 1, 1, 1, 1, 1, 1, 1} };
        test.bits.b8 = 0;
        if (test.value != 0xFE) {
            fprintf(stderr, &quot;Test 2 expected 0xfe, got %#04hhx\n&quot;, test.value);
            ++failures;
        }
    }

    return failures != 0;
}

We need tests of the most-significant bit, and setting as well as resetting bits.  I'll leave that as an exercise.