Array based queue implementation with C++20
Avoid unnecessary copies
enqueue() takes its parameter by value, which means the caller has to make a copy. Then it has to store that value in the array, making another copy. You can avoid copies by replacing the function that takes a value with two overloads, one for const references and one for rvalue references:
void enqueue(const_reference_type val) {
    // same as before
    ...
}

void enqueue(value_type &amp;&amp;val) {
    // mostly the same
    ...
    // but now we move:
    data[current_size++] = std::move(val);
}

The first version will make just one copy, the second one will use move assignment, which might avoid an expensive copy.
Similarly, dequeue() makes a copy. You can avoid this by splitting this function in two; one that returns a reference to the head element so the caller can read it without having to copy it, and another function that just removes the head element. You already have a head(), so then just modify dequeue() to be:
void dequeue() {
    // mostly the same as before, except without returning anything
    ...
}

This is also why std::queue provides a separate front() and pop() function. Also, doing it this way makes the requirements for T less strict; it now only requires it to be std::default_constructible and std::movable.
Make it look like std::queue
Programming is much easier if everything has a similar interface. Consider renaming your member functions to match that of std::queue:

enqueue() -&gt; push()
dequeue() -&gt; pop()
head() -&gt; front()
tail() -&gt; back()

You could also add emplace() and swap() to make it almost a drop-in replacement.