Do custom metrics have dimensions?

Yes, and they always have had. The CloudWatch agent, however, didn’t always support publishing custom dimensions.

See Publishing custom metrics in the CloudWatch User Guide:

In custom metrics, the –dimensions parameter is common. A dimension further clarifies what the metric is and what data it stores. You can have up to 30 dimensions assigned to one metric, and each dimension is defined by a name and value pair.

It seems custom metrics have had dimensions since launch in 2011. See New – Custom Metrics for Amazon CloudWatch from the AWS News blog:

CloudWatch metrics are scoped within namespaces, and can be further qualified by up to 10 dimensions. … Dimensions are name-value pairs that can be used to expand on a metric’s identity. For example, latency could be tracked on a per-host basis by adding a dimension to the example above:

$ mon-put-data -namespace App1 -metric-name Latency -dimensions "Host=host1" -value 104
$ mon-put-data -namespace App1 -metric-name Latency -dimensions "Host=host2" -value 180
$ mon-put-data -namespace App1 -metric-name Latency -dimensions "Host=host3" -value 90

Across these dimensions, custom metrics are less aggregable than the built-in metrics.

See Amazon CloudWatch concepts:

For metrics produced by certain AWS services, such as Amazon EC2, CloudWatch can aggregate data across dimensions. For example, if you search for metrics in the AWS/EC2 namespace but do not specify any dimensions, CloudWatch aggregates all data for the specified metric to create the statistic that you requested. CloudWatch does not aggregate across dimensions for your custom metrics.

You can only retrieve statistics using combinations of dimensions that you specifically published. When you retrieve statistics, specify the same values for the namespace, metric name, and dimension parameters that were used when the metrics were created. … The exception is by using the metric math SEARCH function, which can retrieve statistics for multiple metrics. For more information, see Using search expressions in graphs.

I think my confusion about the answer to this question was caused by the CloudWatch Agent. I first used it some time in 2016 or 2017. According to its release notes, it gained features for custom dimensions on 2017-12-05.

========================================================================
Amazon CloudWatch Agent 1.70.0 (2017-12-05)
========================================================================

New Features and Enhancements:

[...]

* Optionally adds EC2 instance information as CloudWatch Dimensions
  (instance ID, image ID (AMI), instance type, AutoScaling Group name)

* Optionally adds customizable CloudWatch Dimensions

* Provides customizable aggregation on CloudWatch Dimensions
  (producing instance-level vs fleet-level metrics in Amazon
  CloudWatch)

I never got around to using the new feature, so I had been unsure of this until now!