Here's some bad ways and some good ways to handle this.
The Bad Ways
Stick HTML in your code.
my $form_htm = <<EOF
<input value="$in{firstname}" name="firstname">
<input value="$in{surname}" name="surname">
...etc...
EOF
This was a very common thing to do. It has major problems. First, it welds together the logic of the program with what is displayed. There's either only one way to display it, or your code gets increasingly complicated trying to allow multiple ways to display it. You're running into this problem.
Second, it means in order to change how it looks you need a developer involved. You can't have separate UX people working on the HTML and CSS layout, they also need to know Perl or get a Perl developer involved. This makes it very expensive and slow to make otherwise simple HTML changes.
Third, with the code to generate the HTML scattered around it makes it difficult to figure out how each page comes to be. If you want to change a form in a page, you have to hunt around in the code to find how it's generated.
Fourth, it's slow. Every single page has to be generated by the server.
Stick code in your HTML.
<%class>
has 'amount';
has 'name';
</%class>
Dear <% $.name %>,
We are pleased to inform you that you have won $<% sprintf("%.2f", $.amount) %>!
Sincerely,
The Lottery Commission
<%init>
die "amount must be a positive value!" unless $.amount > 0;
</%init>
This what PHP and Mason do. It's better than sticking HTML in your code, but it still has major problems. It can be used well, but it encourages you to put WAY too much logic into the templates. This scatters the logic around making it hard to understand what's going on. Code in the templates is hard to document and test. It also mixes code into the templates which again blurs the line between UX person and developer.
The Good Ways
MVC: Model-View-Controller
Model-View-Controller provides a clear delineation between the data and logic (the "model") and how it's displayed (the "view"). These are connected with a bit of code to get data from the model and send it off to the view as variables (the "controller").
This is one of the most successful ways of putting together a web site. Ruby On Rails and Catalyst are built around this.
I'd recommend Dancer. It's much smaller and easier to understand than Catalyst. Since it provides less it's easier to adapt an existing program to use it. It provides the view and controller, the model is up to you.
There's two good ways to put together the view.
An HTML Template.
The usual way is to put your entire HTML page in a template, much like we saw before, but instead of it having its own code for getting data it's instead passed data to use. It has a limited language for manipulating that data.
In Perl this is generally Template Toolkit.
Dear [% name %],
It has come to our attention that your account is in
arrears to the sum of [% debt %].
Please settle your account before [% deadline %] or we
will be forced to revoke your Licence to Thrill.
The Management.
Now UX people can manage HTML templates on their own. They still have to learn a template language, but it's at least a well documented one. The limited language discourages putting too much code in the templates creating a sort of firewall between the View and the rest of the code.
But all the work to format the template still must be done on the server side. And UX people still have to learn a special template language. Which brings us to the best way.
Pure HTML, CSS, and Javascript.
In this mode, instead of processing a template on the server side and inserting variables into it, the HTML has Javascript which requests the data it needs and displays it as it sees fit. This has great advantages.
It completely divorces the view from the rest of the code. This gives the UX folks great control over how the application behaves. They decide what data each page needs. They decide how it's manipulated and displayed. Developers can focus on providing the data.
The other advantage is using Javascript. Javascript has become the defacto programming language that web designers must know. UX folks don't need to learn a special template language, they use the same HTML, CSS, and Javascript they've always used.
Finally, this means the bulk of the formatting work is done on the client side reducing the load on your server.
The main disadvantage is this requires a major re-architecture of an existing system. Instead of producing HTML pages, your server code now produces JSON for the Javascript to manipulate via REST requests.
Hybrid Template + Javascript
A good hybrid for an existing system is to use templates, but instead of using a special template language, use Javascript. Simple scalar variables like names and amounts can still be provided as template variables, but larger units like lists and hashes are injected into the template as JSON.
For example, this template...
var people = [% people %]
would be provided with people = encode_json(\@people)'
and it might expand to:
var people = ["Jack", "Jill", "Jane", "Joe"]
Then the UX person can do whatever they want with this Javascript people
array.
On the downside, this still gives the developers too much control over how the site works, because they decide what templates are used and what data they're given, and it still means the templates have to be expanded server side.
On the upside it lets UX people manipulate that data in ways they're comfortable with, it enforces a clear separation between the view and the rest of the code, and you can convert existing code to use these sorts of templates a piece at a time.